windows-nt/Source/XPSP1/NT/ds/security/services/smartcrd/sclogon/sclogon.cpp

3152 lines
70 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (C) Microsoft Corporation, 1996 - 1999
Module Name:
ScLogon
Abstract:
This module provides helper functions for use by winlogon (GINA, Kerberos)
Author:
Amanda Matlosz (amatlosz) 10/22/1997
Environment:
Win32, C++ w/ Exceptions
Notes:
03-11-98 Wrap calls to GetLastError() to workaround bug where LastErr gets
clobbered. Added event logging to make logon smoother.
04-02-98 Removed all references to WinVerifyTrust; this is something
Kerberos itself is responsible for.
--*/
/////////////////////////////////////////////////////////////////////////////
//
// Includes
#if !defined(_AMD64_) && !defined(_X86_) && !defined(_IA64_)
#define _X86_ 1
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#ifndef UNICODE
#define UNICODE
#endif
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
extern "C" {
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntlsa.h>
}
#include <windows.h>
#include <winscard.h>
#include <wincrypt.h>
#include <softpub.h>
#include <stddef.h>
#include <crtdbg.h>
#include "sclogon.h"
#include "unicodes.h"
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <tchar.h>
#ifndef KP_KEYEXCHANGE_PIN
#define KP_KEYEXCHANGE_PIN 32
#else
#if 32 != KP_KEYEXCHANGE_PIN
#error Invalid KP_KEYEXCHANGE_PIN assumption
#endif
#endif
#ifndef CRYPT_SILENT
#define CRYPT_SILENT 0x40
#else
#if 0x40 != CRYPT_SILENT
#error Duplicate CRYPT_SILENT definition
#endif
#endif
#ifndef SCARD_PROVIDER_CSP
#define SCARD_PROVIDER_CSP 2
#else
#if 2 != SCARD_PROVIDER_CSP
#error Invalid SCARD_PROVIDER_CSP definition
#endif
#endif
#if defined(DBG) || defined(DEBUG)
BOOL SCDebug = TRUE;
#define DebugPrint(a) _DebugPrint a
void
__cdecl
_DebugPrint(
LPCSTR szFormat,
...
)
{
if (SCDebug) {
CHAR szBuffer[512];
va_list ap;
va_start(ap, szFormat);
vsprintf(szBuffer, szFormat, ap);
OutputDebugStringA(szBuffer);
}
}
#else
#define DebugPrint(a)
#endif
// TODO: The following logging is still proving useful.
// TODO: leave in for B3: integrate more tightly w/ winlogon/kerberos ??
#include <sclmsg.h>
// A Global class used to maintain internal state.
class CSCLogonInit
{
public:
// Runs at image creation
CSCLogonInit(
BOOL *pfResult)
{
m_hCrypt = NULL;
*pfResult = TRUE;
};
// Runs at image termination
~CSCLogonInit()
{
Release();
};
// Cleans up current state.
void
Release(
void)
{
if (NULL != m_hCrypt)
{
CryptReleaseContext(m_hCrypt, 0);
m_hCrypt = NULL;
}
}
// Relinquish control of the crypto context.
HCRYPTPROV
RelinquishCryptCtx(
LogonInfo* pLogon)
{
HCRYPTPROV hProv;
hProv = CryptCtx(pLogon);
m_hCrypt = NULL;
return hProv;
};
// Get the crypto context, creating it if it's not there.
HCRYPTPROV
CryptCtx(
LogonInfo* pLogon)
{
HCRYPTPROV hProv;
LPCTSTR szRdr = NULL;
LPCTSTR szCntr = NULL;
LPTSTR szFQCN = NULL;
LONG lLen = 0;
if (NULL == m_hCrypt)
{
BOOL fSts;
// Prepare FullyQualifiedContainerName for CryptAcCntx call
szRdr = GetReaderName((LPBYTE)pLogon);
szCntr = GetContainerName((LPBYTE)pLogon);
lLen = (lstrlen(szRdr) + lstrlen(szCntr) + 10)*sizeof(TCHAR);
szFQCN = (LPTSTR)LocalAlloc(LPTR, lLen);
if (NULL != szFQCN)
{
wsprintf(szFQCN, TEXT("\\\\.\\%s\\%s"), szRdr, szCntr);
fSts = CryptAcquireContext(
&m_hCrypt,
szFQCN,
GetCSPName((LPBYTE)pLogon),
PROV_RSA_FULL, // ?TODO? from pbLogonInfo
CRYPT_SILENT | CRYPT_MACHINE_KEYSET
);
LocalFree(szFQCN);
}
else
{
fSts = FALSE;
}
}
hProv = m_hCrypt;
return hProv;
}
protected:
HCRYPTPROV m_hCrypt;
};
// For tracing errors in ScHelper*
NTSTATUS LogEvent(NTSTATUS NtErr, DWORD dwEventID)
{
DWORD dwErr;
//
// Convert the error back to a Win32 error
//
switch (NtErr)
{
case STATUS_INVALID_PARAMETER:
dwErr = ERROR_INVALID_DATA;
break;
case STATUS_SMARTCARD_SUBSYSTEM_FAILURE:
// A Cryptxxx API just failed
dwErr = GetLastError();
switch (dwErr)
{
case SCARD_W_WRONG_CHV:
case SCARD_E_INVALID_CHV:
NtErr = STATUS_SMARTCARD_WRONG_PIN;
break;
case SCARD_W_CHV_BLOCKED:
NtErr = STATUS_SMARTCARD_CARD_BLOCKED;
break;
case SCARD_W_REMOVED_CARD:
case SCARD_E_NO_SMARTCARD:
NtErr = STATUS_SMARTCARD_NO_CARD;
break;
case NTE_BAD_KEYSET:
case NTE_KEYSET_NOT_DEF:
NtErr = STATUS_SMARTCARD_NO_KEY_CONTAINER;
break;
case SCARD_E_NO_SUCH_CERTIFICATE:
case SCARD_E_CERTIFICATE_UNAVAILABLE:
NtErr = STATUS_SMARTCARD_NO_CERTIFICATE;
break;
case NTE_NO_KEY:
NtErr = STATUS_SMARTCARD_NO_KEYSET;
break;
case SCARD_E_TIMEOUT:
case SCARD_F_COMM_ERROR:
case SCARD_E_COMM_DATA_LOST:
NtErr = STATUS_SMARTCARD_IO_ERROR;
break;
//default:
// Nothing, leave NtErr unchanged
}
break;
case STATUS_INSUFFICIENT_RESOURCES:
case STATUS_NO_MEMORY:
dwErr = ERROR_OUTOFMEMORY;
break;
case STATUS_BUFFER_TOO_SMALL:
dwErr = SEC_E_BUFFER_TOO_SMALL;
break;
default:
dwErr = SCARD_E_UNEXPECTED;
}
if (0 == dwErr)
{
return NtErr;
}
//
// Initialize log as necessary
//
HKEY hKey;
DWORD disp;
long err = RegCreateKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("System\\CurrentControlSet\\Services\\EventLog\\Application\\Smart Card Logon"),
0,
TEXT(""),
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&hKey,
&disp
);
if (ERROR_SUCCESS != err)
{
return NtErr;
}
if (disp == REG_CREATED_NEW_KEY)
{
PBYTE l_szModulePath = (PBYTE)TEXT("%SystemRoot%\\System32\\scarddlg.dll");
ULONG l_uLen = (_tcslen((LPCTSTR)l_szModulePath) + 1)*sizeof(TCHAR);
RegSetValueEx(
hKey,
TEXT("EventMessageFile"),
0,
REG_EXPAND_SZ,
l_szModulePath,
l_uLen
);
disp = (DWORD)(
EVENTLOG_ERROR_TYPE |
EVENTLOG_WARNING_TYPE |
EVENTLOG_INFORMATION_TYPE
);
RegSetValueEx(
hKey,
TEXT("TypesSupported"),
0,
REG_DWORD,
(PBYTE) &disp,
sizeof(DWORD)
);
}
RegCloseKey(hKey);
HANDLE hEventSource = RegisterEventSource(
NULL,
TEXT("Smart Card Logon")
);
if (NULL != hEventSource)
{
DWORD dwLen = 0;
LPTSTR szErrorString = NULL;
TCHAR szBuffer[2+8+1]; // Enough for "0x????????"
dwLen = FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
dwErr,
LANG_NEUTRAL,
(LPTSTR)&szErrorString,
0,
NULL);
if (dwLen == 0)
{
_stprintf(szBuffer, _T("0x%08lX"), dwErr);
szErrorString = szBuffer;
}
ReportEvent(
hEventSource,
EVENTLOG_ERROR_TYPE,
0, // event category
dwEventID, // event identifier // resourceID for the messagetable entry...
NULL, // user security identifier (optional)
1, // number of strings to merge with message
sizeof(long), // size of binary data, in bytes
(LPCTSTR*)&szErrorString, // array of strings to merge with message
(LPVOID)&dwErr // address of binary data
);
DeregisterEventSource(hEventSource);
if ((NULL != szErrorString) && (szErrorString != szBuffer))
{
LocalFree((LPVOID)szErrorString);
}
}
return NtErr;
}
//////////////////////////////////////////////////////////////////////////////
//
// Structs
//////////////////////////////////////////////////////////////////////////////
//
// Functions
//
// Internal helpers: called by the ScLogon APIs to perform certain tedious work
/*++
GetReaderName:
GetCardName:
GetContainerName:
GetCSPName:
: Intended for accessing the LogonInformation glob
Author:
Amanda Matlosz
Note:
Some of these are made available to outside callers; see sclogon.h
--*/
extern "C"
PBYTE
WINAPI
ScBuildLogonInfo(
LPCTSTR szCard,
LPCTSTR szReader,
LPCTSTR szContainer,
LPCTSTR szCSP)
{
// No assumptions are made regarding the values of the incoming parameters;
// At this point, it is legal for them all to be empty.
// It is also possible that NULL values are being passed in -- if this is the case,
// they must be replaced with empty strings.
LPCTSTR szCardI = TEXT("");
LPCTSTR szReaderI = TEXT("");
LPCTSTR szContainerI = TEXT("");
LPCTSTR szCSPI = TEXT("");
if (NULL != szCard)
{
szCardI = szCard;
}
if (NULL != szReader)
{
szReaderI = szReader;
}
if (NULL != szContainer)
{
szContainerI = szContainer;
}
if (NULL != szCSP)
{
szCSPI = szCSP;
}
//
// Build the LogonInfo glob using strings (or empty strings)
//
DWORD cbLi = offsetof(LogonInfo, bBuffer)
+ (lstrlen(szCardI) + 1) * sizeof(TCHAR)
+ (lstrlen(szReaderI) + 1) * sizeof(TCHAR)
+ (lstrlen(szContainerI) + 1) * sizeof(TCHAR)
+ (lstrlen(szCSPI) + 1) * sizeof(TCHAR);
LogonInfo* pLI = (LogonInfo*)LocalAlloc(LPTR, cbLi);
if (NULL == pLI)
{
return NULL;
}
pLI->ContextInformation = NULL;
pLI->dwLogonInfoLen = cbLi;
LPTSTR pBuffer = pLI->bBuffer;
pLI->nCardNameOffset = 0;
lstrcpy(pBuffer, szCardI);
pBuffer += (lstrlen(szCardI)+1);
pLI->nReaderNameOffset = (ULONG) (pBuffer-pLI->bBuffer);
lstrcpy(pBuffer, szReaderI);
pBuffer += (lstrlen(szReaderI)+1);
pLI->nContainerNameOffset = (ULONG) (pBuffer-pLI->bBuffer);
lstrcpy(pBuffer, szContainerI);
pBuffer += (lstrlen(szContainerI)+1);
pLI->nCSPNameOffset = (ULONG) (pBuffer-pLI->bBuffer);
lstrcpy(pBuffer, szCSPI);
pBuffer += (lstrlen(szCSPI)+1);
_ASSERTE(cbLi == (DWORD)((LPBYTE)pBuffer - (LPBYTE)pLI));
return (PBYTE)pLI;
}
LPCTSTR WINAPI GetReaderName(PBYTE pbLogonInfo)
{
LogonInfo* pLI = (LogonInfo*)pbLogonInfo;
if (NULL == pLI)
{
return NULL;
}
return &pLI->bBuffer[pLI->nReaderNameOffset];
};
LPCTSTR WINAPI GetCardName(PBYTE pbLogonInfo)
{
LogonInfo* pLI = (LogonInfo*)pbLogonInfo;
if (NULL == pLI)
{
return NULL;
}
return &pLI->bBuffer[pLI->nCardNameOffset];
};
LPCTSTR WINAPI GetContainerName(PBYTE pbLogonInfo)
{
LogonInfo* pLI = (LogonInfo*)pbLogonInfo;
if (NULL == pLI)
{
return NULL;
}
return &pLI->bBuffer[pLI->nContainerNameOffset];
};
LPCTSTR WINAPI GetCSPName(PBYTE pbLogonInfo)
{
LogonInfo* pLI = (LogonInfo*)pbLogonInfo;
if (NULL == pLI)
{
return NULL;
}
return &pLI->bBuffer[pLI->nCSPNameOffset];
};
/*++
BuildCertContext:
Generates a certificate context with (static) keyprov info suitable for
CertStore-based operations.
If the PIN is provided, it is assumed the hProv (if provided) has not had the
PIN parameter set...
Arguments:
hProv -- must be a valid HCRYPTPROV
pucPIN -- may be empty; used to set the PIN for hProv
pbCert -- assumed to be a valid certificate; must not be NULL
dwCertLen
CertificateContext -- pointer to a pointer to the resultant CertContext
Return Value:
NTSTATUS indicating STATUS_SUCCESS or error (see winerror.h or scarderr.h)
Author:
Amanda Matlosz
Note:
--*/
NTSTATUS
BuildCertContext(
IN HCRYPTPROV hProv,
IN PUNICODE_STRING pucPIN,
IN PBYTE pbCert,
IN DWORD dwCertLen,
OUT PCCERT_CONTEXT *CertificateContext
)
{
NTSTATUS lResult = STATUS_SUCCESS;
BOOL fSts = FALSE;
CRYPT_KEY_PROV_INFO KeyProvInfo;
LPSTR szContainerName = NULL;
LPSTR szProvName = NULL;
CUnicodeString wszContainerName, wszProvName;
DWORD cbContainerName, cbProvName;
//
// Check params
//
if ((NULL == hProv) || (NULL == pbCert || 0 == dwCertLen))
{
ASSERT(FALSE);
lResult = STATUS_INVALID_PARAMETER;
goto ErrorExit;
}
//
// Convert the certificate into a Cert Context.
//
*CertificateContext = CertCreateCertificateContext(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
pbCert,
dwCertLen);
if (NULL == *CertificateContext)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// Associate cryptprovider w/ the private key property of this cert
//
// ... need the container name
fSts = CryptGetProvParam(
hProv,
PP_CONTAINER,
NULL, // out
&cbContainerName, // in/out
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
szContainerName = (LPSTR)LocalAlloc(LPTR, cbContainerName);
if (NULL == szContainerName)
{
lResult = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
fSts = CryptGetProvParam(
hProv,
PP_CONTAINER,
(PBYTE)szContainerName, // out
&cbContainerName, // in/out
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
wszContainerName = szContainerName;
// ... need the provider name
fSts = CryptGetProvParam(
hProv,
PP_NAME,
NULL, // out
&cbProvName, // in/out
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
szProvName = (LPSTR)LocalAlloc(LPTR, cbProvName);
if (NULL == szProvName)
{
lResult = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
fSts = CryptGetProvParam(
hProv,
PP_NAME,
(PBYTE)szProvName, // out
&cbProvName, // in/out
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
wszProvName = szProvName;
//
// Set the cert context properties to reflect the prov info
//
KeyProvInfo.pwszContainerName = (LPWSTR)(LPCWSTR)wszContainerName;
KeyProvInfo.pwszProvName = (LPWSTR)(LPCWSTR)wszProvName;
KeyProvInfo.dwProvType = PROV_RSA_FULL;
KeyProvInfo.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
KeyProvInfo.cProvParam = 0;
KeyProvInfo.rgProvParam = NULL;
KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE;
KeyProvInfo.dwFlags |= CERT_SET_KEY_CONTEXT_PROP_ID;
fSts = CertSetCertificateContextProperty(
*CertificateContext,
CERT_KEY_PROV_INFO_PROP_ID,
0,
(void *)&KeyProvInfo);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
// the cert's been incorrectly created -- scrap it.
CertFreeCertificateContext(*CertificateContext);
*CertificateContext = NULL;
goto ErrorExit;
}
CERT_KEY_CONTEXT certKeyContext;
certKeyContext.cbSize = sizeof(CERT_KEY_CONTEXT);
certKeyContext.hCryptProv = hProv;
certKeyContext.dwKeySpec = KeyProvInfo.dwKeySpec;
fSts = CertSetCertificateContextProperty(
*CertificateContext,
CERT_KEY_CONTEXT_PROP_ID,
0,
(void *)&certKeyContext);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
// the cert's been incorrectly created -- scrap it.
CertFreeCertificateContext(*CertificateContext);
*CertificateContext = NULL;
goto ErrorExit;
}
ErrorExit:
if(NULL != szContainerName)
{
LocalFree(szContainerName);
szContainerName = NULL;
}
if(NULL != szProvName)
{
LocalFree(szProvName);
szProvName = NULL;
}
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(lResult, (DWORD)EVENT_ID_BUILDCC);
}
return lResult;
}
///////////////////////////////////////////////////////////////////////////////
//
// ScLogon APIs
//
/*++
ScHelperInitializeContext:
Prepares contextual information to be used by LSA while handling this
smart card session.
Arguments:
None.
Return Value:
None
Author:
Richard Ward
Note:
Used by LSA.
--*/
NTSTATUS WINAPI
ScHelperInitializeContext(
IN OUT PBYTE pbLogonInfo,
IN ULONG cbLogonInfo
)
{
ULONG AllowedSize;
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
if ((cbLogonInfo < sizeof(ULONG)) ||
(cbLogonInfo != pLI->dwLogonInfoLen))
{
return(STATUS_INVALID_PARAMETER);
}
AllowedSize = (cbLogonInfo - sizeof(LogonInfo) ) / sizeof(TCHAR) + sizeof(DWORD) ;
//
// Verify the other fields of the logon info
//
if ((pLI->nCardNameOffset > pLI->nReaderNameOffset) ||
(pLI->bBuffer[pLI->nReaderNameOffset-1] != TEXT('\0')))
{
return(STATUS_INVALID_PARAMETER);
}
if ((pLI->nReaderNameOffset > pLI->nContainerNameOffset) ||
(pLI->bBuffer[pLI->nContainerNameOffset-1] != TEXT('\0')))
{
return(STATUS_INVALID_PARAMETER);
}
if ((pLI->nContainerNameOffset > pLI->nCSPNameOffset) ||
(pLI->bBuffer[pLI->nCSPNameOffset-1] != TEXT('\0')))
{
return(STATUS_INVALID_PARAMETER);
}
if ((pLI->nCSPNameOffset > AllowedSize) ||
(pLI->bBuffer[AllowedSize-1] != TEXT('\0')))
{
return(STATUS_INVALID_PARAMETER);
}
_ASSERTE(pLI->ContextInformation == NULL);
BOOL fResult = 0;
pLI->ContextInformation = new CSCLogonInit(&fResult);
if (pLI->ContextInformation == NULL)
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
else
{
if (!fResult)
{
delete pLI->ContextInformation;
pLI->ContextInformation = NULL;
return(STATUS_INSUFFICIENT_RESOURCES);
}
}
return(STATUS_SUCCESS);
}
/*++
ScHelperRelease:
Releases contextual information used by LSA while handling this
smart card session.
Arguments:
None.
Return Value:
None
Author:
Richard Ward
Note:
Used by LSA.
--*/
VOID WINAPI
ScHelperRelease(
IN OUT PBYTE pbLogonInfo
)
{
_ASSERTE(NULL != pbLogonInfo);
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
if (LogonInit != NULL)
{
LogonInit->Release();
delete LogonInit;
pLI->ContextInformation = NULL;
}
}
/*++
ScHelperGetCertFromLogonInfo:
Returns a CertificateContext for the cert on the card specified by the
LogonInfo. Creates the cert context by calling BuildCertContext,
which generates a certificate context with (static) keyprov info
suitable for CertStore-based operations.
Arguments:
pucPIN may need the PIN to get a cert off certain SCs
Return Value:
None
Author:
Amanda Matlosz
Note:
Used by LSA.
--*/
NTSTATUS WINAPI
ScHelperGetCertFromLogonInfo(
IN PBYTE pbLogonInfo,
IN PUNICODE_STRING pucPIN,
OUT PCCERT_CONTEXT *CertificateContext
)
{
_ASSERTE(NULL != pbLogonInfo);
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
BOOL fSts;
NTSTATUS lResult = STATUS_SUCCESS;
PCCERT_CONTEXT pCertCtx = NULL;
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
LPBYTE pbCert = NULL;
DWORD cbCertLen;
//
// Make sure we've got a Crypto Provider up and running.
//
hProv = LogonInit->RelinquishCryptCtx(pLI);
if (NULL == hProv)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// Get the key handle.
//
fSts = CryptGetUserKey(
hProv,
AT_KEYEXCHANGE,
&hKey);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// Upload the certificate.
//
fSts = CryptGetKeyParam(
hKey,
KP_CERTIFICATE,
NULL,
&cbCertLen,
0);
if (!fSts)
{
DWORD dwGLE = GetLastError();
if (ERROR_MORE_DATA != dwGLE)
{
if (NTE_NOT_FOUND == dwGLE)
{
SetLastError(SCARD_E_NO_SUCH_CERTIFICATE);
}
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
pbCert = (LPBYTE)LocalAlloc(LPTR, cbCertLen);
if (NULL == pbCert)
{
lResult = STATUS_NO_MEMORY;
goto ErrorExit;
}
fSts = CryptGetKeyParam(
hKey,
KP_CERTIFICATE,
pbCert,
&cbCertLen,
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
lResult = BuildCertContext(
hProv,
pucPIN,
pbCert,
cbCertLen,
&pCertCtx);
if (NT_SUCCESS(lResult))
{
// The cert context will take care of the crypt context now.
hProv = NULL;
}
//
// Clean up and return.
//
ErrorExit:
*CertificateContext = pCertCtx;
// Do this early so GetLastError is not clobbered
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(lResult, (DWORD)EVENT_ID_GETCERT);
}
if (NULL != hKey)
{
CryptDestroyKey(hKey);
}
if (NULL != hProv)
{
CryptReleaseContext(hProv, 0);
}
if (NULL != pbCert)
{
LocalFree(pbCert);
}
return lResult;
}
/*++
ScHelperGetProvParam:
This API wraps the CryptGetProvParam routine for use with a smart card.
Arguments:
pucPIN supplies a Unicode string containing the card's PIN.
pbLogonInfo supplies the information required to identify the card, csp,
etc. It cannot be NULL.
The other parameters are identical to CryptGetProvParam
Return Value:
A STATUS_SUCECSS for success, or an error
--*/
NTSTATUS WINAPI
ScHelperGetProvParam(
IN PUNICODE_STRING pucPIN,
IN PBYTE pbLogonInfo,
DWORD dwParam,
BYTE*pbData,
DWORD *pdwDataLen,
DWORD dwFlags
)
{
_ASSERTE(NULL != pbLogonInfo);
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTPROV hProv = NULL;
BOOL fSts;
hProv = LogonInit->CryptCtx(pLI);
if (NULL == hProv)
{
return LogEvent(STATUS_SMARTCARD_SUBSYSTEM_FAILURE, (DWORD)EVENT_ID_GETPROVPARAM);
}
fSts = CryptGetProvParam(
hProv,
dwParam,
pbData,
pdwDataLen,
dwFlags
);
if (!fSts)
{
if (GetLastError() == ERROR_NO_MORE_ITEMS)
{
return (STATUS_NO_MORE_ENTRIES);
}
else
{
return LogEvent(STATUS_SMARTCARD_SUBSYSTEM_FAILURE, (DWORD)EVENT_ID_GETPROVPARAM);
}
}
return(STATUS_SUCCESS);
}
/*++
ScHelperVerifyCard:
This API provides an easy way to verify the integrity of the card
identified by pbLogonInfo (ie, that it has the private key associated
w/ the public key contained in the certificate it returned via
ScHelperGetCertFromLogonInfo) and, in so doing, authenticates the user
to the card.
Arguments:
pucPIN supplies a Unicode string containing the card's PIN.
CertificateContext supplies the cert context received via
ScHelperGetCertFromLogonInfo.
hCertStore supplies the handle of a cert store which contains a CTL to
use during certificate verification, or NULL to use the system default
store.
pbLogonInfo supplies the information required to identify the card, csp,
etc. It cannot be NULL.
Return Value:
A 32-bit value indicating whether or not the service completed successfully.
STATUS_SUCCESS is returned on successful completion. Otherwise, the value
represents an error condition.
--*/
NTSTATUS WINAPI
ScHelperVerifyCard(
IN PUNICODE_STRING pucPIN,
IN PCCERT_CONTEXT CertificateContext,
IN HCERTSTORE hCertStore,
IN PBYTE pbLogonInfo
)
{
_ASSERTE(NULL != pbLogonInfo);
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTHASH hHash = NULL;
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
PBYTE pbBlob = NULL;
ULONG ulBlobLen = 32;
PBYTE pbSignature = NULL;
ULONG ulSigLen = 0;
BOOL fSts;
//
// Make sure we've got a Crypto Provider up and running.
//
hProv = LogonInit->CryptCtx(pLI);
if (NULL == hProv)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// Generate a random key blob as the message to sign
//
pbBlob = (LPBYTE)LocalAlloc(LPTR, ulBlobLen);
if (NULL == pbBlob)
{
lResult = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
fSts = CryptGenRandom(hProv, ulBlobLen, pbBlob);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// The card signs a hash of the message...
//
lResult = ScHelperSignMessage(
pucPIN,
pbLogonInfo,
NULL,
CALG_MD5,
pbBlob,
ulBlobLen,
pbSignature,
&ulSigLen);
if (STATUS_BUFFER_TOO_SMALL != lResult)
{
goto ErrorExit;
}
pbSignature = (LPBYTE)LocalAlloc(LPTR, ulSigLen);
if (NULL == pbSignature)
{
lResult = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
lResult = ScHelperSignMessage(
pucPIN,
pbLogonInfo,
NULL,
CALG_MD5,
pbBlob,
ulBlobLen,
pbSignature,
&ulSigLen);
if (!NT_SUCCESS(lResult))
{
goto ErrorExit;
}
//
// Verify the signature is correct
//
lResult = ScHelperVerifyMessage(
pbLogonInfo,
NULL,
CertificateContext,
CALG_MD5,
pbBlob,
ulBlobLen,
pbSignature,
ulSigLen);
//
// Clean up and return.
//
ErrorExit:
// Do this early so GetLastError is not clobbered
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(lResult, (DWORD)EVENT_ID_VERIFYCARD);
}
if (NULL != hKey)
{
CryptDestroyKey(hKey);
}
if (NULL != pbSignature)
{
LocalFree(pbSignature);
}
if (NULL != pbBlob)
{
LocalFree(pbBlob);
}
return lResult;
}
NTSTATUS WINAPI
ScHelperGenRandBits(
IN PBYTE pbLogonInfo,
IN OUT ScHelper_RandomCredBits* psc_rcb
)
{
_ASSERTE(NULL != pbLogonInfo);
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTPROV hProv = NULL;
BOOL fSts = FALSE;
//
// Make sure we've got a Crypto Provider up and running.
//
hProv = LogonInit->CryptCtx(pLI);
if (NULL == hProv)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
memset(psc_rcb, 0, sizeof(*psc_rcb));
fSts = CryptGenRandom(hProv, 32, psc_rcb->bR1);
if (fSts)
{
fSts = CryptGenRandom(hProv, 32, psc_rcb->bR2);
}
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
}
ErrorExit:
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(lResult, (DWORD)EVENT_ID_GENRANDBITS);
}
return lResult;
}
/*++
ScHelperCreateCredKeys:
This routine (called by ScHelperVerifyCardAndCreds and
ScHelperEncryptCredentials) munges a R1 and R2 to derive symmetric keys
for encrypting and decrypting KDC creds, and or genearting an HMAC.
Arguments:
pucPIN supplies a Unicode string containing the card's PIN.
pbLogonInfo supplies the information required to identify the card, csp,
etc. It cannot be NULL.
psc_rcb supplies the R1 and R2, previously generated by a call to
ScHelperGenRandBits.
phHmacKey recieves the generated HMAC key.
phRc4Key receives the generated RC4 key.
Return Value:
A 32-bit value indicating whether or not the service completed successfully.
STATUS_SUCCESS is returned on successful completion. Otherwise, the value
represents an error condition.
Remarks:
You may supply a value of NULL to EncryptedData to receive only the
required size of the EncryptedData buffer.
Author:
Amanda Matlosz (amatlosz) 6/23/1999
--*/
NTSTATUS WINAPI
ScHelperCreateCredKeys(
IN PUNICODE_STRING pucPIN,
IN PBYTE pbLogonInfo,
IN ScHelper_RandomCredBits* psc_rcb,
IN OUT HCRYPTKEY* phHmacKey,
IN OUT HCRYPTKEY* phRc4Key,
IN OUT HCRYPTPROV* phProv
)
{
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTHASH hHash = NULL;
HCRYPTPROV hProv = NULL;
PBYTE pbR1Sig = NULL;
DWORD dwR1SigLen = 0;
HCRYPTHASH hKHash = NULL;
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CUnicodeString szPin(pucPIN);
BOOL fSts = FALSE;
*phProv = NULL;
// check params
if (NULL == psc_rcb || NULL == phHmacKey || NULL == phRc4Key)
{
return(STATUS_INVALID_PARAMETER);
}
// Get hProv for smart card
if (NULL != pucPIN)
{
if (!szPin.Valid())
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
}
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
hProv = LogonInit->CryptCtx(pLI);
if (NULL == hProv)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
// Sign R1 w/ smart card
fSts = CryptCreateHash(
hProv,
CALG_SHA1,
NULL,
NULL,
&hHash);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
fSts = CryptHashData(
hHash,
psc_rcb->bR1,
32, // TODO: const
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// Declare the PIN.
//
if (NULL != pucPIN)
{
fSts = CryptSetProvParam(
hProv,
PP_KEYEXCHANGE_PIN,
(LPBYTE)((LPCSTR)szPin),
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
fSts = CryptSignHash(
hHash,
AT_KEYEXCHANGE,
NULL,
0,
NULL,
&dwR1SigLen);
// if (fSts || ERROR_MORE_DATA != GetLastError())
if (0 >= dwR1SigLen)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
pbR1Sig = (LPBYTE)LocalAlloc(LPTR, dwR1SigLen);
if (NULL == pbR1Sig)
{
lResult = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
fSts = CryptSignHash(
hHash,
AT_KEYEXCHANGE,
NULL,
0,
pbR1Sig,
&dwR1SigLen);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
// TODO: sigR1 is the key to hash R2 with;
// for now, just hash 'em together; use generic CSP
fSts = CryptAcquireContext(
phProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
fSts = CryptCreateHash(
*phProv,
CALG_SHA1,
NULL,
NULL,
&hKHash
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
fSts = CryptHashData(
hKHash,
pbR1Sig,
dwR1SigLen,
NULL
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
fSts = CryptHashData(
hKHash,
psc_rcb->bR2,
32, // TODO: use a const
NULL
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
// create the rc4 key for the cred&hmac encryption
fSts = CryptDeriveKey(
*phProv,
CALG_RC4, // stream cipher,
hKHash,
NULL,
phRc4Key
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
// create the key for the HMAC from the hash of R1&2
fSts = CryptDeriveKey(
*phProv,
CALG_RC2,
hKHash,
NULL,
phHmacKey
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
ErrorExit:
//
// cleanup
//
if (NULL != hHash)
{
CryptDestroyHash(hHash);
}
if (NULL != hKHash)
{
CryptDestroyHash(hKHash);
}
if (NULL != pbR1Sig)
{
LocalFree(pbR1Sig);
}
return lResult;
}
NTSTATUS WINAPI
ScHelperCreateCredHMAC(
IN HCRYPTPROV hProv,
IN HCRYPTKEY hHmacKey,
IN PBYTE CleartextData,
IN ULONG CleartextDataSize,
IN OUT PBYTE* ppbHmac,
IN OUT DWORD* pdwHmacLen
)
{
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTHASH hHMAC = NULL;
HMAC_INFO hmac_info;
BOOL fSts = FALSE;
fSts = CryptCreateHash(
hProv,
CALG_HMAC,
hHmacKey,
NULL,
&hHMAC
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
memset(&hmac_info, 0, sizeof(HMAC_INFO));
hmac_info.HashAlgid = CALG_SHA1;
fSts = CryptSetHashParam(
hHMAC,
HP_HMAC_INFO,
(PBYTE)&hmac_info,
NULL
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
fSts = CryptHashData(
hHMAC,
CleartextData,
CleartextDataSize,
NULL);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
fSts = CryptGetHashParam(
hHMAC,
HP_HASHVAL,
*ppbHmac,
pdwHmacLen,
NULL
);
if (0 >= *pdwHmacLen)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
*ppbHmac = (PBYTE)LocalAlloc(LPTR, *pdwHmacLen);
if (NULL == *ppbHmac)
{
lResult = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
fSts = CryptGetHashParam(
hHMAC,
HP_HASHVAL,
*ppbHmac,
pdwHmacLen,
NULL
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
ErrorExit:
if (NULL != hHMAC)
{
CryptDestroyHash(hHMAC);
}
return lResult;
}
/*++
ScHelperVerifyCardAndCreds:
This routine combines Card Verification and Credential Decryption.
Arguments:
pucPIN supplies a Unicode string containing the card's PIN.
CertificateContext supplies the cert context received via
ScHelperGetCertFromLogonInfo.
hCertStore supplies the handle of a cert store which contains a CTL to
use during certificate verification, or NULL to use the system
default store.
pbLogonInfo supplies the information required to identify the card, csp,
etc. It cannot be NULL.
EncryptedData receives the encrypted credential blob.
EncryptedDataSize supplies the size of the EncryptedData buffer in
bytes, and receives the actual size of the encrypted blob.
CleartextData supplies a credential blob to be encrypted.
CleartextDataSize supplies the size of the blob, in bytes.
Return Value:
A 32-bit value indicating whether or not the service completed successfully.
STATUS_SUCCESS is returned on successful completion. Otherwise, the value
represents an error condition.
Remarks:
You may supply a value of NULL to EncryptedData to receive only the
required size of the EncryptedData buffer.
Author:
Doug Barlow (dbarlow) 5/24/1999
--*/
NTSTATUS WINAPI
ScHelperVerifyCardAndCreds(
IN PUNICODE_STRING pucPIN,
IN PCCERT_CONTEXT CertificateContext,
IN HCERTSTORE hCertStore,
IN PBYTE pbLogonInfo,
IN PBYTE EncryptedData,
IN ULONG EncryptedDataSize,
OUT OPTIONAL PBYTE CleartextData,
OUT PULONG CleartextDataSize
)
{
NTSTATUS lResult = STATUS_SUCCESS;
// Verify the Card
lResult = ScHelperVerifyCard(
pucPIN,
CertificateContext,
hCertStore,
pbLogonInfo);
// Decrypt the Creds
if (NT_SUCCESS(lResult))
{
lResult = ScHelperDecryptCredentials(
pucPIN,
CertificateContext,
hCertStore,
pbLogonInfo,
EncryptedData,
EncryptedDataSize,
CleartextData,
CleartextDataSize);
}
return lResult;
}
/*++
ScHelperDecryptCredentials:
This routine decrypts an encrypted credential blob.
Arguments:
pucPIN supplies a Unicode string containing the card's PIN.
CertificateContext supplies the cert context received via
ScHelperGetCertFromLogonInfo.
hCertStore supplies the handle of a cert store which contains a CTL to
use during certificate verification, or NULL to use the system
default store.
EncryptedData supplies the encrypted credential blob.
EncryptedDataSize supplies the length of the encrypted credential blob,
in bytes.
CleartextData receives the decrypted credential blob.
CleartextDataSize supplies the length of the CleartextData buffer, and
receives the actual length of returned decrypted credential blob.
Return Value:
A 32-bit value indicating whether or not the service completed successfully.
STATUS_SUCCESS is returned on successful completion. Otherwise, the value
represents an error condition.
Remarks:
You may supply a value of NULL to CleartextData to receive only the
required size of the buffer in CleartextDataSize.
Author:
Doug Barlow (dbarlow) 5/24/1999
--*/
NTSTATUS WINAPI
ScHelperDecryptCredentials(
IN PUNICODE_STRING pucPIN,
IN PCCERT_CONTEXT CertificateContext,
IN HCERTSTORE hCertStore,
IN PBYTE pbLogonInfo,
IN PBYTE EncryptedData,
IN ULONG EncryptedDataSize,
OUT OPTIONAL PBYTE CleartextData,
OUT PULONG CleartextDataSize)
{
NTSTATUS lResult = STATUS_SUCCESS;
PBYTE pbCredBlob = NULL;
DWORD dwCredBlobSize = 0;
PBYTE pbHmac = NULL; // the HMAC stored with the cred blob
DWORD dwHmacSize = NULL; // size of HMAC stored with cred blob
PBYTE pbNewHmac = NULL; // HMAC generated from cred blob for verify
DWORD dwNewHmacSize = 0; // size of gen'd HMAC
PBYTE pb = NULL;
DWORD dw = 0;
PBYTE pbPlainCred = NULL;
DWORD dwPlainCredSize = 0;
HCRYPTKEY hHmacKey = NULL;
HCRYPTKEY hRc4Key = NULL;
HCRYPTPROV hGenProv = NULL;
BOOL fSts = FALSE;
// pull the SCH_RCB out of the EncryptedData blob
ScHelper_RandomCredBits* psch_rcb = (ScHelper_RandomCredBits*)EncryptedData;
// and build a private copy of the blob itself
dwCredBlobSize = EncryptedDataSize - sizeof(ScHelper_RandomCredBits);
pbCredBlob = (PBYTE)LocalAlloc(LPTR, dwCredBlobSize);
if (NULL == pbCredBlob)
{
lResult = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
pb = EncryptedData + sizeof(ScHelper_RandomCredBits);
CopyMemory(pbCredBlob, pb, dwCredBlobSize);
//
// Fetch the keys we need to decrypt & verify the cred blob
//
lResult = ScHelperCreateCredKeys(
pucPIN,
pbLogonInfo,
psch_rcb,
&hHmacKey,
&hRc4Key,
&hGenProv
);
if (!NT_SUCCESS(lResult))
{
goto ErrorExit;
}
//
// Decrypt the cred blob
//
fSts = CryptDecrypt(
hRc4Key,
NULL,
TRUE,
NULL,
pbCredBlob,
&dwCredBlobSize);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// pull the HMAC out & verify it
//
dwHmacSize = (DWORD)*pbCredBlob;
pbHmac = pbCredBlob + sizeof(DWORD);
pbPlainCred = pbCredBlob + dwHmacSize + sizeof(DWORD);
dwPlainCredSize = dwCredBlobSize - dwHmacSize - sizeof(DWORD);
lResult = ScHelperCreateCredHMAC(
hGenProv,
hHmacKey,
pbPlainCred,
dwPlainCredSize,
&pbNewHmac,
&dwNewHmacSize);
if (!NT_SUCCESS(lResult))
{
goto ErrorExit;
}
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
if (dwNewHmacSize == dwHmacSize)
{
for (dw = 0;
(dw < dwNewHmacSize) && ((BYTE)*(pbHmac+dw)==(BYTE)*(pbNewHmac+dw));
dw++);
if (dwNewHmacSize == dw)
{
// verification succeeded!
lResult = STATUS_SUCCESS;
}
}
if (!NT_SUCCESS(lResult))
{
goto ErrorExit;
}
//
// return the decrypted blob or just its length, as necessary
//
if ((NULL != CleartextData) && (0 < *CleartextDataSize))
{
if (*CleartextDataSize >= dwPlainCredSize)
{
CopyMemory(CleartextData, pbPlainCred, dwPlainCredSize);
}
else
lResult = STATUS_BUFFER_TOO_SMALL;
}
else
{
lResult = STATUS_BUFFER_TOO_SMALL;
}
*CleartextDataSize = dwPlainCredSize;
//
// Cleanup and return
//
ErrorExit:
if (NULL != pbNewHmac)
{
LocalFree(pbNewHmac);
}
if (NULL != hHmacKey)
{
CryptDestroyKey(hHmacKey);
}
if (NULL != hRc4Key)
{
CryptDestroyKey(hRc4Key);
}
if (NULL != hGenProv)
{
CryptReleaseContext(hGenProv, NULL);
}
return lResult;
}
/*++
ScHelperEncryptCredentials:
This routine encrypts a credential blob.
Arguments:
pucPIN supplies a Unicode string containing the card's PIN.
CertificateContext supplies the cert context received via
ScHelperGetCertFromLogonInfo.
hCertStore supplies the handle of a cert store which contains a CTL to
use during certificate verification, or NULL to use the system
default store.
CleartextData supplies the cleartext credential blob.
CleartextDataSize supplies the length of the cleartext credential blob,
in bytes.
EncryptedData receives the encrypted credential blob.
EncryptedDataSize supplies the length of the EncryptedData buffer, and
receives the actual length of returned encrypted credential blob.
Return Value:
A 32-bit value indicating whether or not the service completed successfully.
STATUS_SUCCESS is returned on successful completion. Otherwise, the value
represents an error condition.
Remarks:
You may supply a value of NULL to EncryptedData to receive only the
required size of the buffer in EncryptedDataSize.
Author:
Doug Barlow (dbarlow) 5/24/1999
--*/
NTSTATUS WINAPI
ScHelperEncryptCredentials(
IN PUNICODE_STRING pucPIN,
IN PCCERT_CONTEXT CertificateContext,
IN HCERTSTORE hCertStore,
IN ScHelper_RandomCredBits* psch_rcb,
IN PBYTE pbLogonInfo,
IN PBYTE CleartextData,
IN ULONG CleartextDataSize,
OUT OPTIONAL PBYTE EncryptedData,
OUT PULONG EncryptedDataSize)
{
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTPROV hProv = NULL;
BOOL fSts;
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
ULONG SignedEncryptedCredSize = 0;
PBYTE SignedEncryptedCred = NULL; // encrypted cred&sig, !including R1+R2
HCRYPTKEY hHmacKey = NULL;
HCRYPTKEY hRc4Key = NULL;
PBYTE pbHmac = NULL;
DWORD dwHmacLen = 0;
PBYTE pbCredsAndHmac = NULL;
DWORD dwCredsAndHmacLen = 0;
DWORD dwEncryptedCredSize = 0;
PBYTE pb = NULL;
// parameter checking?
//
// do stuff to determine size required for SignedEncryptedCred
//
lResult = ScHelperCreateCredKeys(
pucPIN,
pbLogonInfo,
psch_rcb,
&hHmacKey,
&hRc4Key,
&hProv
);
if (!NT_SUCCESS(lResult))
{
goto ErrorExit;
}
// HMAC creds
lResult = ScHelperCreateCredHMAC(
hProv,
hHmacKey,
CleartextData,
CleartextDataSize,
&pbHmac,
&dwHmacLen);
if (!NT_SUCCESS(lResult))
{
goto ErrorExit;
}
// make a buffer with creds and HMAC
pbCredsAndHmac = NULL;
dwCredsAndHmacLen = dwHmacLen + CleartextDataSize + sizeof(DWORD);
pbCredsAndHmac = (PBYTE)LocalAlloc(LPTR, dwCredsAndHmacLen);
if (NULL == pbCredsAndHmac)
{
lResult = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
pb = pbCredsAndHmac;
CopyMemory(pb, &dwHmacLen, sizeof(DWORD));
pb += sizeof(DWORD);
CopyMemory(pb, pbHmac, dwHmacLen);
pb += dwHmacLen;
CopyMemory(pb, CleartextData, CleartextDataSize);
// Encrypt creds+HMAC
dwEncryptedCredSize = dwCredsAndHmacLen;
// After CryptEncrypt, dwCredsAndHmacLen describes the length of the data
// to encrypt and dwEncryptedCredSize describes the req'd buffer length
// TODO: VERIFY THE HANDLING OF dwEncryptedCredSize and dwCresAndHmacLen
fSts = CryptEncrypt(
hRc4Key,
NULL,
TRUE,
NULL,
pbCredsAndHmac,
&dwEncryptedCredSize,
dwCredsAndHmacLen
);
if (!fSts)
{
if (GetLastError() != ERROR_MORE_DATA)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
//
// Create the final blob for return, or inform user of size, as necessary
//
if ((NULL != EncryptedData) && (0 < *EncryptedDataSize))
{
if (*EncryptedDataSize >= dwEncryptedCredSize + sizeof(ScHelper_RandomCredBits))
{
// the user gave us enough space for the whole thing.
// if the previous CryptEncrypt failed with ERROR_MORE_DATA
// we can now do something about it...
if (!fSts)
{
// resize pbCredsAndHmac
LocalFree(pbCredsAndHmac);
pbCredsAndHmac = (PBYTE)LocalAlloc(LPTR, dwCredsAndHmacLen);
if (NULL == pbCredsAndHmac)
{
lResult = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorExit;
}
// reset pbCredsAndHmac
pb = pbCredsAndHmac;
CopyMemory(pb, &dwHmacLen, sizeof(DWORD));
pb += sizeof(DWORD);
CopyMemory(pb, pbHmac, dwHmacLen);
pb += dwHmacLen;
CopyMemory(pb, CleartextData, CleartextDataSize);
// re-encrypt CredsAndHmac
fSts = CryptEncrypt(
hRc4Key,
NULL,
TRUE,
NULL,
pbCredsAndHmac,
&dwCredsAndHmacLen, // length of data
dwEncryptedCredSize // length of buffer
);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
pb = EncryptedData;
CopyMemory(pb, (PBYTE)psch_rcb, sizeof(ScHelper_RandomCredBits));
pb += sizeof(ScHelper_RandomCredBits);
CopyMemory(pb, pbCredsAndHmac, dwCredsAndHmacLen);
}
else
{
lResult = STATUS_BUFFER_TOO_SMALL;
}
}
else
{
lResult = STATUS_BUFFER_TOO_SMALL;
}
*EncryptedDataSize = dwEncryptedCredSize + sizeof(ScHelper_RandomCredBits);
ErrorExit:
// clean up!
if (NULL != pbCredsAndHmac)
{
LocalFree(pbCredsAndHmac);
}
if (NULL != pbHmac)
{
LocalFree(pbHmac);
}
if (NULL != hRc4Key)
{
CryptDestroyKey(hRc4Key);
}
if (NULL != hHmacKey)
{
CryptDestroyKey(hHmacKey);
}
if (NULL != hProv)
{
CryptReleaseContext(hProv, NULL);
}
return lResult;
}
/*++
ScHelperSignMessage:
ScHelperSignMessage() needs the logoninfo and PIN in order to find the card
that will do the signing...
Arguments:
pucPIN may need the PIN to get a cert off certain SCs
Return Value:
"success" or "failure"
Author:
Amanda Matlosz
Note:
Used by LSA.
--*/
NTSTATUS WINAPI
ScHelperSignMessage(
IN PUNICODE_STRING pucPIN,
IN OPTIONAL PBYTE pbLogonInfo,
IN OPTIONAL HCRYPTPROV Provider,
IN ULONG Algorithm,
IN PBYTE Buffer,
IN ULONG BufferLength,
OUT PBYTE Signature,
OUT PULONG SignatureLength
)
{
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTHASH hHash = NULL;
HCRYPTPROV hProv = NULL;
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CUnicodeString szPin(pucPIN);
BOOL fSts;
//
// Make sure we've got a Crypto Provider up and running.
//
if (ARGUMENT_PRESENT(Provider))
{
hProv = Provider;
}
else
{
if (NULL != pucPIN)
{
if (!szPin.Valid())
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
}
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
hProv = LogonInit->CryptCtx(pLI);
if (NULL == hProv)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
//
// We'll need a hash handle, too.
//
fSts = CryptCreateHash(
hProv,
Algorithm,
NULL, // HCRYPTKEY (used for keyed algs, like block ciphers
0, // reserved for future use
&hHash);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// Hash the input data.
//
fSts = CryptHashData(
hHash,
Buffer,
BufferLength,
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
if (!ARGUMENT_PRESENT(Provider))
{
//
// Declare the PIN.
//
if (NULL != pucPIN)
{
fSts = CryptSetProvParam(
hProv,
PP_KEYEXCHANGE_PIN,
(LPBYTE)((LPCSTR)szPin),
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
}
//
// OK, sign it with the exchange key from the smart card or the supplied signature key. ????
//
fSts = CryptSignHash(
hHash,
AT_KEYEXCHANGE,
NULL,
0,
Signature,
SignatureLength);
if (!fSts)
{
if (GetLastError() == ERROR_MORE_DATA)
{
lResult = STATUS_BUFFER_TOO_SMALL;
}
else
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
}
}
//
// All done, clean up and return.
//
ErrorExit:
// Do this early so GetLastError is not clobbered
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(
lResult,
(DWORD)((ARGUMENT_PRESENT(Provider))?EVENT_ID_SIGNMSG_NOSC:EVENT_ID_SIGNMSG)
);
}
if (NULL != hHash)
{
CryptDestroyHash(hHash);
}
return lResult;
}
/*++
ScHelperVerifyMessage:
// ScHelperVerifyMessage() returns STATUS_SUCCESS if the signature provided is
// the hash of the buffer encrypted by the owner of the cert.
Arguments:
pucPIN may need the PIN to get a cert off certain SCs
Return Value:
"success" or "failure"
Author:
Amanda Matlosz
Note:
Used by LSA.
--*/
NTSTATUS WINAPI
ScHelperVerifyMessage(
IN OPTIONAL PBYTE pbLogonInfo,
IN OPTIONAL HCRYPTPROV Provider,
IN PCCERT_CONTEXT CertificateContext,
IN ULONG Algorithm,
IN PBYTE Buffer,
IN ULONG BufferLength,
IN PBYTE Signature,
IN ULONG SignatureLength
)
{
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
HCRYPTHASH hHash = NULL;
PCERT_PUBLIC_KEY_INFO pInfo = NULL;
BOOL fSts;
NTSTATUS lResult = STATUS_SUCCESS;
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
//
// Make sure we've got a Crypto Provider up and running.
//
if (ARGUMENT_PRESENT(Provider))
{
hProv = Provider;
}
else
{
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
hProv = LogonInit->CryptCtx(pLI);
if (NULL == hProv)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
//
// Convert the certificate handle into a Public Key handle.
//
fSts = CryptImportPublicKeyInfo(
hProv,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&CertificateContext->pCertInfo->SubjectPublicKeyInfo,
&hKey);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// We'll need a hash handle, too.
//
fSts = CryptCreateHash(
hProv,
Algorithm,
NULL, // HCRYPTKEY (used for keyed algs, like block ciphers
0, // reserved for future use
&hHash);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// Hash the input data.
//
fSts = CryptHashData(
hHash,
Buffer,
BufferLength,
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// So is this signature any good?
//
fSts = CryptVerifySignature(
hHash,
Signature,
SignatureLength,
hKey,
NULL,
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// All done, clean up and return.
//
ErrorExit:
// Do this early so GetLastError is not clobbered
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(
lResult,
(DWORD)((ARGUMENT_PRESENT(Provider))?EVENT_ID_VERIFYMSG_NOSC:EVENT_ID_VERIFYMSG)
);
}
if (NULL != hHash)
{
CryptDestroyHash(hHash);
}
if (NULL != hKey)
{
CryptDestroyKey(hKey);
}
return lResult;
}
/*++
ScHelperSignPkcsMessage:
ScHelperSignMessage() needs the logoninfo and PIN in order to find the card
that will do the signing...
Arguments:
pucPIN may need the PIN to get a cert off certain SCs
Return Value:
"success" or "failure"
Author:
Amanda Matlosz
Note:
Used by LSA.
--*/
NTSTATUS WINAPI
ScHelperSignPkcsMessage(
IN OPTIONAL PUNICODE_STRING pucPIN,
IN OPTIONAL PBYTE pbLogonInfo,
IN OPTIONAL HCRYPTPROV Provider,
IN PCCERT_CONTEXT Certificate,
IN PCRYPT_ALGORITHM_IDENTIFIER Algorithm,
IN DWORD dwSignMessageFlags,
IN PBYTE Buffer,
IN ULONG BufferLength,
OUT OPTIONAL PBYTE SignedBuffer,
OUT OPTIONAL PULONG SignedBufferLength
)
{
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTPROV hProv = NULL;
BOOL fSts;
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CRYPT_SIGN_MESSAGE_PARA Parameter = {0};
CUnicodeString szPin(pucPIN);
const BYTE * BufferArray = Buffer;
if (NULL != pucPIN)
{
if (!szPin.Valid())
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
}
//
// Make sure we've got a Crypto Provider up and running.
//
if (ARGUMENT_PRESENT(Provider))
{
hProv = Provider;
}
else
{
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
hProv = LogonInit->CryptCtx(pLI);
if (NULL == hProv)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// Declare the PIN.
//
if (NULL != pucPIN)
{
fSts = CryptSetProvParam(
hProv,
PP_KEYEXCHANGE_PIN,
(LPBYTE)((LPCSTR)szPin),
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
}
//
// Sign the message
//
Parameter.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
Parameter.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
Parameter.pSigningCert = Certificate;
Parameter.HashAlgorithm = *Algorithm;
Parameter.cMsgCert = 1;
Parameter.rgpMsgCert = &Certificate;
Parameter.dwFlags = dwSignMessageFlags;
fSts = CryptSignMessage(
&Parameter,
FALSE, // no detached signature
1, // one buffer to sign
&BufferArray,
&BufferLength,
SignedBuffer,
SignedBufferLength);
if (!fSts)
{
if (GetLastError() == ERROR_MORE_DATA)
{
lResult = STATUS_BUFFER_TOO_SMALL;
}
else
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
}
goto ErrorExit;
}
//
// All done, clean up and return.
//
ErrorExit:
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(lResult, (DWORD)EVENT_ID_SIGNMSG);
}
return lResult;
}
/*++
ScHelperVerifyPkcsMessage:
// ScHelperVerifyMessage() returns STATUS_SUCCESS if the signature provided is
// the hash of the buffer encrypted by the owner of the cert.
Arguments:
pucPIN may need the PIN to get a cert off certain SCs
Return Value:
"success" or "failure"
Author:
Amanda Matlosz
Note:
Used by LSA.
--*/
NTSTATUS WINAPI
ScHelperVerifyPkcsMessage(
IN OPTIONAL PBYTE pbLogonInfo,
IN OPTIONAL HCRYPTPROV Provider,
IN PBYTE Buffer,
IN ULONG BufferLength,
OUT OPTIONAL PBYTE DecodedBuffer,
OUT OPTIONAL PULONG DecodedBufferLength,
OUT OPTIONAL PCCERT_CONTEXT * CertificateContext
)
{
CRYPT_VERIFY_MESSAGE_PARA Parameter = {0};
BOOL fSts;
NTSTATUS lResult = STATUS_SUCCESS;
Parameter.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
Parameter.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
Parameter.hCryptProv = NULL;
//
// Indicate that we want to get the certificate from the message
// cert store.
//
Parameter.pfnGetSignerCertificate = NULL;
fSts = CryptVerifyMessageSignature(
&Parameter,
0, // only check first signer
Buffer,
BufferLength,
DecodedBuffer,
DecodedBufferLength,
CertificateContext
);
if (!fSts)
{
if (GetLastError() == ERROR_MORE_DATA)
{
lResult = STATUS_BUFFER_TOO_SMALL;
}
else
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
}
goto ErrorExit;
}
//
// All done, clean up and return.
//
ErrorExit:
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(lResult, (DWORD)EVENT_ID_VERIFYMSG);
}
return lResult;
}
/*++
ScHelperEncryptMessage:
Encrypts a message with the public key associated w/ the provided
certificate. The resultant encoding is PKCS-7 compliant.
Arguments:
pucPIN may need the PIN to get a cert off certain SCs
Return Value:
"success" or "failure"
Author:
Amanda Matlosz (AMatlosz) 1-06-98
Note:
Either pbLogonInfo or Provided must be set; if both are set,
Provider is used.
Algorithm expects a CRYPT_ALGORITHM_IDENTIFIER cai;
If there are no parameters to the alg, cai.Parameters.cbData *must* be 0;
CALG_RC4, no parameters:
cai.pszObjId = szOID_RSA_RC4;
cai.Parameters.cbData = 0;
Used by LSA.
--*/
NTSTATUS WINAPI
ScHelperEncryptMessage(
IN OPTIONAL PBYTE pbLogonInfo,
IN OPTIONAL HCRYPTPROV Provider,
IN PCCERT_CONTEXT CertificateContext,
IN PCRYPT_ALGORITHM_IDENTIFIER Algorithm,
IN PBYTE Buffer, // The data to encrypt
IN ULONG BufferLength, // The length of that data
OUT PBYTE CipherText, // Receives the formatted CipherText
IN PULONG pCipherLength // Supplies size of CipherText buffer
) // Receives length of actual CipherText
{
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTPROV hProv = NULL;
BOOL fSts;
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CRYPT_ENCRYPT_MESSAGE_PARA EncryptPara;
DWORD cbEncryptParaSize = 0;
//
// Make sure we've got a Crypto Provider up and running.
//
if (ARGUMENT_PRESENT(Provider))
{
hProv = Provider;
}
else
{
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
hProv = LogonInit->CryptCtx(pLI);
if (NULL == hProv)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
//
// Encrypt the message
//
cbEncryptParaSize = sizeof(EncryptPara);
memset(&EncryptPara, 0, cbEncryptParaSize);
EncryptPara.cbSize = cbEncryptParaSize;
EncryptPara.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
EncryptPara.hCryptProv = hProv;
EncryptPara.ContentEncryptionAlgorithm = *Algorithm;
fSts = CryptEncryptMessage(
&EncryptPara,
1,
&CertificateContext,
Buffer,
BufferLength,
CipherText,
pCipherLength);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
ErrorExit:
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(
lResult,
(DWORD)((ARGUMENT_PRESENT(Provider))?EVENT_ID_ENCMSG_NOSC:EVENT_ID_ENCMSG)
);
}
return lResult;
}
/*++
ScHelperDecryptMessage :
Deciphers a PKCS-7 encoded message with the private key associated
w/ the provided certificate.
Arguments:
Either pbLogonInfo or Provider must be set; if both are set,
Provider is used.
Return Value:
"success" or "failure"
Author:
Amanda Matlosz (AMatlosz) 1-06-98
Note:
** CertificateContext subtleties: **
CryptDecryptMessage takes as a parameter a pointer to a certificate store;
it will use the first appropriate certificate context it finds in that
store to perform the decryption. In order to make this call, we create a
CertificateStore in memory, and add the provided CertificateContext to it.
CertAddCertificateContextToStore actually places a copy of the certificate
context in the store. In so doing, it strips off any properties that are
not permanent -- if a HCRYPTPROV is associated with the KeyContext of the
source CertificateContext, it will NOT be associated with the KeyContext
of the cert context in the store.
Although this is appropriate behavior in most cases, we need that property
to be kept intact when dealing with Smart Card CSPs (to avoid surprise
"Insert PIN" dialogs), so after adding the CertificateContext to the store,
we turn around and get the CERT_KEY_CONTEXT_PROP_ID from the source
certcontext and (re)set it on the certcontext in the memory store.
** Algorithm notes: **
Algorithm expects a CRYPT_ALGORITHM_IDENTIFIER cai;
If there are no parameters to the alg, cai.Parameters.cbData *must* be 0;
for example: CALG_RC4, no parameters:
cai.pszObjId = szOID_RSA_RC4;
cai.Parameters.cbData = 0;
Used by LSA.
--*/
NTSTATUS WINAPI
ScHelperDecryptMessage(
IN PUNICODE_STRING pucPIN,
IN OPTIONAL PBYTE pbLogonInfo,
IN OPTIONAL HCRYPTPROV Provider,
IN PCCERT_CONTEXT CertificateContext,
IN PBYTE CipherText, // Supplies formatted CipherText
IN ULONG CipherLength, // Supplies the length of the CiperText
OUT PBYTE ClearText, // Receives decrypted message
IN OUT PULONG pClearLength // Supplies length of buffer, receives actual length
)
{
NTSTATUS lResult = STATUS_SUCCESS;
HCRYPTPROV hProv = NULL;
PCCERT_CONTEXT pStoreCertContext = NULL;
HCERTSTORE hCertStore = NULL;
LogonInfo *pLI = (LogonInfo *)pbLogonInfo;
CUnicodeString szPin(pucPIN);
CERT_KEY_CONTEXT CertKeyContext;
DWORD cbData = sizeof(CERT_KEY_CONTEXT); // PhilH swears this will not grow!
BOOL fSts;
//
// Make sure we've got a Crypto Provider up and running.
//
if (ARGUMENT_PRESENT(Provider))
{
hProv = Provider;
}
else
{
if (NULL != pucPIN)
{
if (!szPin.Valid())
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
}
CSCLogonInit * LogonInit = (CSCLogonInit *) pLI->ContextInformation;
hProv = LogonInit->CryptCtx(pLI);
if (NULL == hProv)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
//
// Declare the PIN.
//
if (NULL != pucPIN )
{
fSts = CryptSetProvParam(
hProv,
PP_KEYEXCHANGE_PIN,
(LPBYTE)((LPCSTR)szPin),
0);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
}
//
// Open a temporary certstore to hold this certcontext
//
hCertStore = CertOpenStore(
CERT_STORE_PROV_MEMORY,
0, // not applicable
hProv,
CERT_STORE_NO_CRYPT_RELEASE_FLAG, // auto-release hProv NOT OK
NULL);
if (NULL == hCertStore)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
fSts = CertAddCertificateContextToStore(
hCertStore,
CertificateContext,
CERT_STORE_ADD_ALWAYS,
&pStoreCertContext);
//
// NOW WE NEED TO RESET THE KEY CONTEXT PROPERTY ON THIS CERTCONTEXT
// IN THE MEMORY STORE (see function header/notes) AS APPROPRIATE
//
// ie, IFF the certcontext we were give has the key_context property,
// reset it (and fail if the resetting doesn't work)
//
fSts = CertGetCertificateContextProperty(
CertificateContext,
CERT_KEY_CONTEXT_PROP_ID,
(void *)&CertKeyContext,
&cbData);
if (TRUE == fSts)
{
fSts = CertSetCertificateContextProperty(
pStoreCertContext,
CERT_KEY_CONTEXT_PROP_ID,
CERT_STORE_NO_CRYPT_RELEASE_FLAG, // no auto-release hProv!
(void *)&CertKeyContext);
if (!fSts)
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
goto ErrorExit;
}
}
//
// Decrypt the message
//
CRYPT_DECRYPT_MESSAGE_PARA DecryptPara;
DecryptPara.cbSize = sizeof(DecryptPara);
DecryptPara.dwMsgAndCertEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
DecryptPara.cCertStore = 1;
DecryptPara.rghCertStore = &hCertStore;
fSts = CryptDecryptMessage(
&DecryptPara,
CipherText,
CipherLength,
ClearText,
pClearLength,
NULL);
if (!fSts)
{
if (GetLastError() == ERROR_MORE_DATA)
{
lResult = STATUS_BUFFER_TOO_SMALL;
}
else
{
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
}
goto ErrorExit;
}
ErrorExit:
// Do this early so GetLastError is not clobbered
if (!NT_SUCCESS(lResult))
{
lResult = LogEvent(
lResult,
(DWORD)((ARGUMENT_PRESENT(Provider))?EVENT_ID_DECMSG_NOSC:EVENT_ID_DECMSG)
);
}
if (hCertStore != NULL)
{
fSts = CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
if (!fSts)
{
if (!NT_SUCCESS(lResult))
lResult = STATUS_SMARTCARD_SUBSYSTEM_FAILURE;
}
}
return lResult;
}