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.
 
 
 
 
 
 

12798 lines
368 KiB

//depot/Lab03_DEV/Ds/security/cryptoapi/pki/activex/xenroll/cenroll.cpp#4 - edit change 19979 (text)
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: cenroll.cpp
//
//--------------------------------------------------------------------------
// CEnroll.cpp : Implementation of CCEnroll
#include "stdafx.h"
#include <windows.h>
#include <wincrypt.h>
#include <unicode.h>
#define SECURITY_WIN32
#include <security.h>
#include <aclapi.h>
#include <pvk.h>
#include <wintrust.h>
#include <xasn.h>
#include <autoenr.h>
#include <sddl.h>
#include "xenroll.h"
#include "cenroll.h"
#include "xelib.h"
#include "sfscript.h"
#define NO_OSS_DEBUG
#include <dbgdef.h>
#include <string.h>
#include <assert.h>
static LPVOID (* MyCoTaskMemAlloc)(ULONG) = NULL;
static LPVOID (* MyCoTaskMemRealloc)(LPVOID, ULONG) = NULL;
static void (* MyCoTaskMemFree)(LPVOID) = NULL;
#define MY_HRESULT_FROM_WIN32(a) ((a >= 0x80000000) ? a : HRESULT_FROM_WIN32(a))
#ifndef NTE_TOKEN_KEYSET_STORAGE_FULL
#define NTE_TOKEN_KEYSET_STORAGE_FULL _HRESULT_TYPEDEF_(0x80090023L)
#endif
#define CEnrollLocalScope(ScopeName) struct ScopeName##TheLocalScope { public
#define CEnrollEndLocalScope } local
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
#pragma warning(disable:4213) // nonstandard extension used : cast on l-value
static LPSTR MBFromWide(LPCWSTR wsz) {
LPSTR sz = NULL;
DWORD cb = 0;
assert(wsz != NULL);
if(wsz == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return(NULL);
}
if( (cb = WideCharToMultiByte(0, 0, wsz, -1, NULL, 0, NULL, NULL)) == 0 ||
(sz = (char *) MyCoTaskMemAlloc(cb)) == NULL ||
(cb = WideCharToMultiByte(0, 0, wsz, -1, sz, cb, NULL, NULL)) == 0 ) {
if(GetLastError() == ERROR_SUCCESS)
SetLastError(ERROR_OUTOFMEMORY);
return(NULL);
}
return(sz);
}
static LPWSTR WideFromMB(LPCSTR sz) {
DWORD cch = 0;
LPWSTR wsz = NULL;
assert(sz != NULL);
if(sz == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return(NULL);
}
if( (cch = MultiByteToWideChar(0, 0, sz, -1, NULL, 0)) == 0 ||
(wsz = (WCHAR *) MyCoTaskMemAlloc(cch * sizeof(WCHAR))) == NULL ||
(cch = MultiByteToWideChar(0, 0, sz, -1, wsz, cch)) == 0) {
if(GetLastError() == ERROR_SUCCESS)
SetLastError(ERROR_OUTOFMEMORY);
return(NULL);
}
return(wsz);
}
static BSTR
BSTRFromMB(LPCSTR sz)
{
BSTR bstr = NULL;
DWORD cch = 0;
WCHAR *pwsz = NULL;
BOOL fFail = FALSE;
assert(sz != NULL);
if(sz == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return(NULL);
}
while (TRUE)
{
if(0 == (cch = MultiByteToWideChar(0, 0, sz, -1, pwsz, cch)))
{
//error
fFail = TRUE;
break;
}
if (NULL != pwsz)
{
//done
break;
}
pwsz = (WCHAR *)LocalAlloc(LMEM_FIXED, cch * sizeof(WCHAR));
if (NULL == pwsz)
{
//error
if(GetLastError() == ERROR_SUCCESS)
SetLastError(ERROR_OUTOFMEMORY);
break;
}
}
if (!fFail && NULL != pwsz)
{
bstr = SysAllocString(pwsz);
if (NULL == bstr)
{
if(GetLastError() == ERROR_SUCCESS)
SetLastError(ERROR_OUTOFMEMORY);
}
}
if (NULL != pwsz)
{
LocalFree(pwsz);
}
return(bstr);
}
static LPWSTR CopyWideString(LPCWSTR wsz) {
size_t cch = 0;
LPWSTR wszOut = NULL;
// shouldn't send in a NULL
assert(wsz != NULL);
if(wsz == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return(NULL);
}
cch = wcslen(wsz) + 1;
if (cch*sizeof(WCHAR) > (ULONG)-1) {
// prevent errors caused by conversion from size_t --> ULONG
SetLastError(ERROR_INVALID_PARAMETER);
return(NULL);
}
if( (wszOut = (LPWSTR) MyCoTaskMemAlloc((ULONG)(sizeof(WCHAR) * cch))) == NULL ) {
SetLastError(ERROR_OUTOFMEMORY);
return(NULL);
}
wcscpy(wszOut, wsz);
return(wszOut);
}
static LPSTR CopyAsciiString(LPCSTR sz) {
size_t cch = 0;
LPSTR szOut = NULL;
// shouldn't send in a NULL
assert(sz != NULL);
if(sz == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return(NULL);
}
cch = strlen(sz) + 1;
if (cch > (ULONG)-1) {
// prevent errors caused by conversion from size_t --> ULONG
SetLastError(ERROR_INVALID_PARAMETER);
return(NULL);
}
if( (szOut = (LPSTR) MyCoTaskMemAlloc((ULONG)cch)) == NULL ) {
SetLastError(ERROR_OUTOFMEMORY);
return(NULL);
}
strcpy(szOut, sz);
return(szOut);
}
static DWORD KeyLocationFromStoreLocation(DWORD dwStoreFlags) {
if(
((CERT_SYSTEM_STORE_LOCATION_MASK & dwStoreFlags) == CERT_SYSTEM_STORE_CURRENT_USER) ||
((CERT_SYSTEM_STORE_LOCATION_MASK & dwStoreFlags) == CERT_SYSTEM_STORE_USERS) ||
((CERT_SYSTEM_STORE_LOCATION_MASK & dwStoreFlags) == CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY)
) {
return(0);
}
// CERT_SYSTEM_STORE_LOCAL_MACHINE
// CERT_SYSTEM_STORE_DOMAIN_POLICY
// CERT_SYSTEM_STORE_CURRENT_SERVICE
// CERT_SYSTEM_STORE_SERVICES
// CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY
return(CRYPT_MACHINE_KEYSET);
}
//modified from myLoadRCString from ca
HRESULT
xeLoadRCString(
IN HINSTANCE hInstance,
IN int iRCId,
OUT WCHAR **ppwsz)
{
#define REALLOCATEBLOCK 512
HRESULT hr;
WCHAR *pwszTemp = NULL;
int sizeTemp;
int size = 0;
int cBlocks = 1;
*ppwsz = NULL;
while (NULL == pwszTemp)
{
sizeTemp = cBlocks * REALLOCATEBLOCK;
pwszTemp = (WCHAR*)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
sizeTemp * sizeof(WCHAR));
if (NULL == pwszTemp)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
size = LoadStringU(
hInstance,
iRCId,
pwszTemp,
sizeTemp);
if (0 == size)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto LoadStringError;
}
if (size < sizeTemp - 1)
{
// ok, size is big enough
break;
}
++cBlocks;
LocalFree(pwszTemp);
pwszTemp = NULL;
}
*ppwsz = (WCHAR*) LocalAlloc(LPTR, (size+1) * sizeof(WCHAR));
if (NULL == *ppwsz)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
// copy it
wcscpy(*ppwsz, pwszTemp);
hr = S_OK;
ErrorReturn:
if (NULL != pwszTemp)
{
LocalFree(pwszTemp);
}
return hr;
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(LoadStringError)
}
HANDLE CCEnroll::CreateOpenFileSafely2(
LPCWSTR pwszFileName,
DWORD idsCreate,
DWORD idsOverwrite)
{
HANDLE hFile = NULL;
WCHAR *pwszMsg = NULL;
WCHAR *pwszFormat = NULL;
WCHAR *pwszTitle = NULL;
WCHAR *pwszSafety = NULL;
DWORD dwAttribs = 0;
BOOL fNotProperFile;
LPCWSTR apwszInsertArray[2];
BOOL fNo;
BOOL fMsgBox;
int idPrefix = IDS_NOTSAFE_WRITE_PREFIX; //default to write prefix
BOOL fCreate = (0xFFFFFFFF != idsCreate) &&
(0xFFFFFFFF != idsOverwrite);
HRESULT hr;
EnterCriticalSection(&m_csXEnroll);
fMsgBox = (m_dwEnabledSafteyOptions != 0);
dwAttribs = GetFileAttributesU(pwszFileName);
if(0xFFFFFFFF == dwAttribs)
{
//file doesn't exist
if (!fCreate)
{
//try to read a non-existing file
//for safety reasons, don't return system error
SetLastError(ERROR_ACCESS_DENIED);
goto InvalidFileError;
}
//if got here, write a new file
if (fMsgBox)
{
hr = xeLoadRCString(hInstanceXEnroll, idsCreate, &pwszFormat);
if (S_OK != hr)
{
goto xeLoadRCStringError;
}
}
}
else
{
//file exists, check if a proper file to write or read
//in either write or read, the following file attrib not proper
fNotProperFile =
(dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ||
(dwAttribs & FILE_ATTRIBUTE_HIDDEN) ||
(dwAttribs & FILE_ATTRIBUTE_SYSTEM);
if (!fNotProperFile)
{
//so far so good
if (fCreate)
{
//write a file
if (0x0 != (dwAttribs & FILE_ATTRIBUTE_READONLY))
{
//don't take read-only and archive
fNotProperFile = TRUE;
}
else
{
//try to overwrite existing file
hr = xeLoadRCString(hInstanceXEnroll, idsOverwrite, &pwszFormat);
if (S_OK != hr)
{
goto xeLoadRCStringError;
}
//enforce popup if overwrite
fMsgBox = TRUE;
}
}
else
{
//read an existing file always violate scripting safety
//it allows detecting file existence
//put out a warning
fMsgBox = TRUE;
hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_OPEN, &pwszFormat);
if (S_OK != hr)
{
goto xeLoadRCStringError;
}
idPrefix = IDS_NOTSAFE_OPEN_PREFIX;
}
}
if (fNotProperFile)
{
//for safety reasons, don't return system error
SetLastError(ERROR_ACCESS_DENIED);
goto InvalidFileError;
}
}
if (fMsgBox)
{
hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
if (S_OK != hr)
{
goto xeLoadRCStringError;
}
hr = xeLoadRCString(hInstanceXEnroll, idPrefix, &pwszSafety);
if (S_OK != hr)
{
goto xeLoadRCStringError;
}
apwszInsertArray[0] = pwszSafety;
apwszInsertArray[1] = pwszFileName;
if (!FormatMessageU(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
pwszFormat,
0,
0,
(LPWSTR) &pwszMsg,
0,
(va_list *)apwszInsertArray))
{
goto FormatMessageError;
}
fNo = (MessageBoxU(
NULL,
pwszMsg,
pwszTitle,
MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES);
if(fNo)
{
SetLastError(ERROR_CANCELLED);
goto CancelError;
}
}
hFile = CreateFileU(
pwszFileName,
fCreate ? GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ,
NULL,
fCreate ? CREATE_ALWAYS : OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE || hFile == NULL)
{
//don't return system error so keep xenroll relative safe for scripting
SetLastError(ERROR_ACCESS_DENIED);
hFile = NULL;
goto CreateFileUError;
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
if(NULL != pwszMsg)
{
LocalFree(pwszMsg);
}
if(NULL != pwszFormat)
{
LocalFree(pwszFormat);
}
if(NULL != pwszTitle)
{
LocalFree(pwszTitle);
}
if(NULL != pwszSafety)
{
LocalFree(pwszSafety);
}
return(hFile);
TRACE_ERROR(CreateFileUError)
TRACE_ERROR(FormatMessageError);
TRACE_ERROR(CancelError)
TRACE_ERROR(InvalidFileError)
TRACE_ERROR(xeLoadRCStringError)
}
HANDLE CCEnroll::CreateOpenFileSafely(
LPCWSTR pwszFileName,
BOOL fCreate)
{
HANDLE hFile = NULL;
WCHAR *pwszMsg = NULL;
DWORD dwAttribs = 0;
BOOL fNotProperFile;
WCHAR *pwszFormat = NULL;
WCHAR *pwszTitle = NULL;
LPCWSTR apwszInsertArray[] = {pwszFileName};
BOOL fNo;
BOOL fMsgBox = 0 != m_dwEnabledSafteyOptions;
BOOL fOverWrite = FALSE;
HRESULT hr;
EnterCriticalSection(&m_csXEnroll);
dwAttribs = GetFileAttributesU(pwszFileName);
if(0xFFFFFFFF == dwAttribs)
{
//file doesn't exist
if (!fCreate)
{
//try to read a non-existing file
//for safety reasons, don't return system error
SetLastError(ERROR_ACCESS_DENIED);
goto InvalidFileError;
}
}
else
{
//file exists, check if a proper file to write or read
//in either write or read, the following file attrib not proper
fNotProperFile =
(dwAttribs & FILE_ATTRIBUTE_DIRECTORY) ||
(dwAttribs & FILE_ATTRIBUTE_HIDDEN) ||
(dwAttribs & FILE_ATTRIBUTE_SYSTEM);
if (!fNotProperFile)
{
//so far so good
if (fCreate)
{
//write a file
if (0x0 != (dwAttribs & FILE_ATTRIBUTE_READONLY))
{
//don't take read-only and archive
fNotProperFile = TRUE;
}
else
{
//try to overwrite existing file
fOverWrite = TRUE;
}
}
}
if (fNotProperFile)
{
//for safety reasons, don't return system error
SetLastError(ERROR_ACCESS_DENIED);
goto InvalidFileError;
}
}
if (fMsgBox)
{
hr = xeLoadRCString(hInstanceXEnroll, IDS_CERTENROLL, &pwszTitle);
if (S_OK != hr)
{
goto xeLoadRCStringError;
}
hr = xeLoadRCString(
hInstanceXEnroll,
fCreate ? IDS_NOTSAFE_WRITE_FORMAT : IDS_NOTSAFE_OPEN_FORMAT,
&pwszFormat);
if (S_OK != hr)
{
goto xeLoadRCStringError;
}
if (!FormatMessageU(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
pwszFormat,
0,
0,
(LPWSTR) &pwszMsg,
0,
(va_list *)apwszInsertArray))
{
goto FormatMessageUError;
}
fNo = (MessageBoxU(
NULL,
pwszMsg,
pwszTitle,
MB_DEFBUTTON2 | MB_YESNO | MB_ICONWARNING) == IDNO);
if(fNo)
{
SetLastError(ERROR_CANCELLED);
goto CancelError;
}
}
if (fCreate && fOverWrite)
{
if (!fMsgBox)
{
hr = xeLoadRCString(hInstanceXEnroll, IDS_CERTENROLL, &pwszTitle);
if (S_OK != hr)
{
goto xeLoadRCStringError;
}
}
//popup overwrite confirmation
hr = xeLoadRCString(hInstanceXEnroll, IDS_OVERWRITE_FORMAT, &pwszFormat);
if (S_OK != hr)
{
goto xeLoadRCStringError;
}
//make sure free before alloc again
if (NULL != pwszMsg)
{
LocalFree(pwszMsg);
pwszMsg = NULL;
}
FormatMessageU(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_STRING |
FORMAT_MESSAGE_ARGUMENT_ARRAY,
pwszFormat,
0,
0,
(LPWSTR) &pwszMsg,
0,
(va_list *)apwszInsertArray);
fNo = (MessageBoxU(
NULL,
pwszMsg,
pwszTitle,
MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2) != IDYES);
if(fNo)
{
SetLastError(ERROR_CANCELLED);
goto CancelError;
}
}
hFile = CreateFileU(
pwszFileName,
fCreate ? GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ,
NULL,
fCreate ? CREATE_ALWAYS : OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE || hFile == NULL)
{
//don't return system error so keep xenroll relative safe for scripting
SetLastError(ERROR_ACCESS_DENIED);
hFile = NULL;
goto CreateFileUError;
}
ErrorReturn:
if(NULL != pwszMsg)
{
LocalFree(pwszMsg);
}
if(NULL != pwszTitle)
{
LocalFree(pwszTitle);
}
if(NULL != pwszFormat)
{
LocalFree(pwszFormat);
}
LeaveCriticalSection(&m_csXEnroll);
return(hFile);
TRACE_ERROR(CreateFileUError)
TRACE_ERROR(CancelError)
TRACE_ERROR(FormatMessageUError);
TRACE_ERROR(InvalidFileError)
TRACE_ERROR(xeLoadRCStringError)
}
HANDLE CCEnroll::CreateFileSafely(
LPCWSTR pwszFileName)
{
return CreateOpenFileSafely(pwszFileName, TRUE); //write
}
HANDLE CCEnroll::OpenFileSafely(
LPCWSTR pwszFileName)
{
return CreateOpenFileSafely(pwszFileName, FALSE); //open
}
void DwordToWide(DWORD dw, LPWSTR lpwstr) {
DWORD i = 0;
DWORD j;
WCHAR wch;
while(dw > 0) {
j = dw % 10;
dw /= 10;
lpwstr[i++] = (WCHAR) (j + L'\0');
}
if( i == 0 )
lpwstr[i++] = L'\0';
lpwstr[i] = 0;
for(j=0, i--; i > j; i--, j++) {
wch = lpwstr[i];
lpwstr[i] = lpwstr[j];
lpwstr[j] = wch;
}
}
//take a name value pair info and return encoded value
HRESULT
xeEncodeNameValuePair(
IN PCRYPT_ENROLLMENT_NAME_VALUE_PAIR pNameValuePair,
OUT BYTE **ppbData,
OUT DWORD *pcbData)
{
HRESULT hr = S_OK;
//init
*ppbData = NULL;
*pcbData = 0;
while (TRUE)
{
if(!CryptEncodeObject(
CRYPT_ASN_ENCODING,
szOID_ENROLLMENT_NAME_VALUE_PAIR,
pNameValuePair,
*ppbData,
pcbData))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto error;
}
if (NULL != *ppbData)
{
break;
}
*ppbData = (BYTE*)MyCoTaskMemAlloc(*pcbData);
if (NULL == *ppbData)
{
hr = E_OUTOFMEMORY;
goto error;
}
}
error:
if (S_OK != hr && NULL != *ppbData)
{
MyCoTaskMemFree(*ppbData);
*ppbData = NULL;
}
return hr;
}
//convert wsz to sz and allocate mem
HRESULT
xeWSZToSZ(
IN LPCWSTR pwsz,
OUT LPSTR *ppsz)
{
HRESULT hr = S_OK;
LONG cc = 0;
//init
*ppsz = NULL;
while (TRUE)
{
cc = WideCharToMultiByte(
GetACP(),
0,
pwsz,
-1,
*ppsz,
cc,
NULL,
NULL);
if (0 >= cc)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto error;
}
if (NULL != *ppsz)
{
break;
}
*ppsz= (CHAR*)MyCoTaskMemAlloc(cc);
if (NULL == *ppsz)
{
hr = E_OUTOFMEMORY;
goto error;
}
}
error:
if (S_OK != hr && NULL != *ppsz)
{
MyCoTaskMemFree(*ppsz);
*ppsz = NULL;
}
return hr;
}
//modified from DecodeFile on certsrv
HRESULT
CCEnroll::xeStringToBinaryFromFile(
IN WCHAR const *pwszfn,
OUT BYTE **ppbOut,
OUT DWORD *pcbOut,
IN DWORD Flags)
{
HANDLE hFile;
HRESULT hr;
CHAR *pchFile = NULL;
BYTE *pbOut = NULL;
DWORD cchFile;
DWORD cbRead;
DWORD cbOut = 0;
hFile = OpenFileSafely(pwszfn);
if (NULL == hFile)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto OpenFileSafelyError;
}
cchFile = GetFileSize(hFile, NULL);
if ((DWORD) -1 == cchFile)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto GetFileSizeError;
}
pchFile = (CHAR *) LocalAlloc(LMEM_FIXED, cchFile);
if (NULL == pchFile)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
if (!ReadFile(hFile, pchFile, cchFile, &cbRead, NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ReadFileError;
}
assert(cbRead <= cchFile);
if (cbRead != cchFile)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_HANDLE_EOF);
goto ReadFileError;
}
if (CRYPT_STRING_BINARY == Flags)
{
pbOut = (BYTE *) pchFile;
cbOut = cchFile;
pchFile = NULL;
}
else
{
// Decode file contents.
while (TRUE)
{
if (!MyCryptStringToBinaryA(
pchFile,
cchFile,
Flags,
pbOut,
&cbOut,
NULL,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CyrptStringToBinaryError;
}
if (NULL != pbOut)
{
//done
break;
}
pbOut = (BYTE*)LocalAlloc(LMEM_FIXED, cbOut);
if (NULL == pbOut)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
}
}
*pcbOut = cbOut;
*ppbOut = pbOut;
pbOut = NULL;
hr = S_OK;
error:
if (INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
}
if (NULL != pchFile)
{
LocalFree(pchFile);
}
if (NULL != pbOut)
{
LocalFree(pbOut);
}
return(hr);
ErrorReturn:
goto error;
TRACE_ERROR(CyrptStringToBinaryError)
TRACE_ERROR(ReadFileError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(GetFileSizeError)
TRACE_ERROR(OpenFileSafelyError)
}
//following two functions handle some APIs not available
//in downlevel client crypt32.dll
typedef VOID
(WINAPI * PFNCertFreeCertificateChain)
(IN PCCERT_CHAIN_CONTEXT pChainContext);
typedef BOOL
(WINAPI * PFNCertGetCertificateChain)
(IN OPTIONAL HCERTCHAINENGINE hChainEngine,
IN PCCERT_CONTEXT pCertContext,
IN OPTIONAL LPFILETIME pTime,
IN OPTIONAL HCERTSTORE hAdditionalStore,
IN PCERT_CHAIN_PARA pChainPara,
IN DWORD dwFlags,
IN LPVOID pvReserved,
OUT PCCERT_CHAIN_CONTEXT* ppChainContext);
typedef BOOL (WINAPI *PFNCertVerifyCertificateChainPolicy) (
LPCSTR pszPolicyOID,
PCCERT_CHAIN_CONTEXT pChainContext,
PCERT_CHAIN_POLICY_PARA pPolicyPara,
PCERT_CHAIN_POLICY_STATUS pPolicyStatus
);
typedef BOOL (*PFNCheckTokenMembership) (
HANDLE TokenHandle, // handle to access token
PSID SidToCheck, // SID
PBOOL IsMember // result
);
typedef BOOL (*PFNSetSecurityDescriptorControl) (
PSECURITY_DESCRIPTOR pSecurityDescriptor, // SD
SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, // control bits
SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet // new control bits
);
VOID
MyCertFreeCertificateChain (
IN PCCERT_CHAIN_CONTEXT pChainContext
)
{
PFNCertFreeCertificateChain pfnCertFreeCertificateChain = NULL;
HMODULE hModule = NULL;
hModule = GetModuleHandle("crypt32.dll");
if (NULL != hModule)
{
pfnCertFreeCertificateChain = (PFNCertFreeCertificateChain)
GetProcAddress(hModule,
"CertFreeCertificateChain");
if (NULL != pfnCertFreeCertificateChain)
{
pfnCertFreeCertificateChain(pChainContext);
}
}
}
BOOL
MyCertGetCertificateChain (
IN OPTIONAL HCERTCHAINENGINE hChainEngine,
IN PCCERT_CONTEXT pCertContext,
IN OPTIONAL LPFILETIME pTime,
IN OPTIONAL HCERTSTORE hAdditionalStore,
IN PCERT_CHAIN_PARA pChainPara,
IN DWORD dwFlags,
IN LPVOID pvReserved,
OUT PCCERT_CHAIN_CONTEXT* ppChainContext
)
{
PFNCertGetCertificateChain pfnCertGetCertificateChain = NULL;
HMODULE hModule = NULL;
hModule = GetModuleHandle("crypt32.dll");
if (NULL != hModule)
{
pfnCertGetCertificateChain = (PFNCertGetCertificateChain)
GetProcAddress(hModule,
"CertGetCertificateChain");
if (NULL != pfnCertGetCertificateChain)
{
return pfnCertGetCertificateChain(
hChainEngine,
pCertContext,
pTime,
hAdditionalStore,
pChainPara,
dwFlags,
pvReserved,
ppChainContext);
}
}
return FALSE;
}
BOOL
MyCertVerifyCertificateChainPolicy(
LPCSTR pszPolicyOID,
PCCERT_CHAIN_CONTEXT pChainContext,
PCERT_CHAIN_POLICY_PARA pPolicyPara,
PCERT_CHAIN_POLICY_STATUS pPolicyStatus
)
{
PFNCertVerifyCertificateChainPolicy pfnCertVerifyCertificateChainPolicy = NULL;
HMODULE hModule = NULL;
hModule = GetModuleHandle("crypt32.dll");
if (NULL != hModule)
{
pfnCertVerifyCertificateChainPolicy = (PFNCertVerifyCertificateChainPolicy)
GetProcAddress(hModule,
"CertVerifyCertificateChainPolicy");
if (NULL != pfnCertVerifyCertificateChainPolicy)
{
return pfnCertVerifyCertificateChainPolicy(
pszPolicyOID,
pChainContext,
pPolicyPara,
pPolicyStatus);
}
}
return FALSE;
}
BOOL
MyCheckTokenMembership(
HANDLE TokenHandle, // handle to access token
PSID SidToCheck, // SID
PBOOL IsMember // result
)
{
PFNCheckTokenMembership pfnCheckTokenMembership = NULL;
HMODULE hModule = NULL;
hModule = GetModuleHandle("advapi32.dll");
if (NULL != hModule)
{
pfnCheckTokenMembership = (PFNCheckTokenMembership)
GetProcAddress(hModule,
"CheckTokenMembership");
if (NULL != pfnCheckTokenMembership)
{
return pfnCheckTokenMembership(
TokenHandle,
SidToCheck,
IsMember);
}
}
return FALSE;
}
BOOL
MySetSecurityDescriptorControl(
PSECURITY_DESCRIPTOR pSecurityDescriptor, // SD
SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest, // control bits
SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet // new control bits
)
{
PFNSetSecurityDescriptorControl pfnSetSecurityDescriptorControl = NULL;
HMODULE hModule = NULL;
hModule = GetModuleHandle("advapi32.dll");
if (NULL != hModule)
{
pfnSetSecurityDescriptorControl = (PFNSetSecurityDescriptorControl)
GetProcAddress(hModule,
"SetSecurityDescriptorControl");
if (NULL != pfnSetSecurityDescriptorControl)
{
return pfnSetSecurityDescriptorControl(
pSecurityDescriptor,
ControlBitsOfInterest,
ControlBitsToSet);
}
}
return FALSE;
}
HRESULT __stdcall CCEnroll::GetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [out] */ DWORD __RPC_FAR *pdwSupportedOptions,
/* [out] */ DWORD __RPC_FAR *pdwEnabledOptions) {
RPC_STATUS rpcStatus;
if(0 != UuidCompare((GUID *) &riid, (GUID *) &IID_IDispatch, &rpcStatus) )
return(E_NOINTERFACE);
*pdwEnabledOptions = m_dwEnabledSafteyOptions;
*pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA;
return(S_OK);
}
HRESULT __stdcall CCEnroll::SetInterfaceSafetyOptions(
/* [in] */ REFIID riid,
/* [in] */ DWORD dwOptionSetMask,
/* [in] */ DWORD dwEnabledOptions) {
RPC_STATUS rpcStatus;
DWORD dwSupport = 0;
if(0 != UuidCompare((GUID *) &riid, (GUID *) &IID_IDispatch, &rpcStatus) )
return(E_NOINTERFACE);
dwSupport = dwOptionSetMask & ~(INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA);
if(dwSupport != 0)
return(E_FAIL);
(DWORD)m_dwEnabledSafteyOptions &= ~dwOptionSetMask;
(DWORD)m_dwEnabledSafteyOptions |= dwEnabledOptions;
return(S_OK);
}
HRESULT
CCEnroll::GetVerifyProv()
{
HRESULT hr;
EnterCriticalSection(&m_csXEnroll);
if (NULL == m_hVerifyProv)
{
if (!CryptAcquireContextU(
&m_hVerifyProv,
NULL,
m_keyProvInfo.pwszProvName,
m_keyProvInfo.dwProvType,
CRYPT_VERIFYCONTEXT))
{
#if DBG
assert(NULL == m_hVerifyProv);
#endif //DBG
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptAcquireContextUError;
}
}
hr = S_OK;
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return hr;
TRACE_ERROR(CryptAcquireContextUError)
}
BOOL CCEnroll::GetCapiHashAndSigAlgId(ALG_ID rgAlg[2]) {
DWORD iHashBest = 0;
ALG_ID arDefaultHash[] = {m_HashAlgId, CALG_SHA1, CALG_MD5};
DWORD cDefaultHash = sizeof(arDefaultHash) / sizeof(DWORD);
HCRYPTPROV hProvU = NULL;
DWORD dwFlags = CRYPT_FIRST;
DWORD i;
PROV_ENUMALGS enumAlgs;
DWORD cb = sizeof(enumAlgs);
BOOL fRet = TRUE;
rgAlg[0] = 0;
rgAlg[1] = 0;
EnterCriticalSection(&m_csXEnroll);
// only get a prov if one wasn't passed in.
if(m_hProv == NULL)
{
HRESULT hr;
hr = GetVerifyProv();
if (S_OK != hr)
{
goto GetVerifyProvError;
}
hProvU = m_hVerifyProv;
}
else
{
// otherwise use the current m_hProv, SCard only likes on
// CryptAcquireContext to be used.
hProvU = m_hProv;
}
cb = sizeof(enumAlgs);
while( CryptGetProvParam(
hProvU,
PP_ENUMALGS,
(BYTE *) &enumAlgs,
&cb,
dwFlags
) ) {
cb = sizeof(enumAlgs);
// not first pass anymore
dwFlags = 0;
// see if this is a hash alg
if( ALG_CLASS_HASH == GET_ALG_CLASS(enumAlgs.aiAlgid) ) {
// get things init with the first hash alg
if(rgAlg[0] == 0) {
rgAlg[0] = enumAlgs.aiAlgid;
iHashBest = cDefaultHash;
}
// pick the best one
for(i=0; i<iHashBest; i++) {
if(arDefaultHash[i] == enumAlgs.aiAlgid) {
rgAlg[0] = enumAlgs.aiAlgid;
iHashBest = i;
break;
}
}
}
// we will only pick up the first signature type
// in general there is only 1 per csp (Ref: JeffSpel)
else if( ALG_CLASS_SIGNATURE == GET_ALG_CLASS(enumAlgs.aiAlgid) ) {
if(rgAlg[1] == 0)
rgAlg[1] = enumAlgs.aiAlgid;
}
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
// some CSPs say they can't sign, but they really can
// so if we have not hashalg or sigalg we will put a default
// in and if the CSP really can't do it, it will error
// this is for backwards compatibility
// default hash to sha1
if(rgAlg[0] == 0)
rgAlg[0] = CALG_SHA1;
// default sig to RSA
if(rgAlg[1] == 0)
rgAlg[1] = CALG_RSA_SIGN;
#if 0
if(rgAlg[0] == 0 || rgAlg[1] == 0) {
SetLastError((DWORD)NTE_BAD_ALGID);
fRet = FALSE;
}
#endif
return(fRet);
TRACE_ERROR(GetVerifyProvError)
}
BOOL CreatePvkProperty(
CRYPT_KEY_PROV_INFO *pKeyProvInfo,
PCRYPT_DATA_BLOB pBlob)
{
WCHAR wszKeySpec[11];
WCHAR wszProvType[11];
DWORD cbContainer;
DWORD cbKeySpec;
DWORD cbProvType;
DWORD cbProvName;
assert(pBlob != NULL);
assert(pKeyProvInfo != NULL);
// convert dwords to strings
DwordToWide(pKeyProvInfo->dwKeySpec, wszKeySpec);
DwordToWide(pKeyProvInfo->dwProvType, wszProvType);
// get total length of string
cbContainer = (DWORD)(wcslen(pKeyProvInfo->pwszContainerName) + 1) * sizeof(WCHAR);
cbKeySpec = (DWORD)(wcslen(wszKeySpec) + 1) * sizeof(WCHAR);
cbProvType = (DWORD)(wcslen(wszProvType) + 1) * sizeof(WCHAR);
cbProvName = (DWORD)(wcslen(pKeyProvInfo->pwszProvName) + 1) * sizeof(WCHAR);
pBlob->cbData =
cbContainer +
cbKeySpec +
cbProvType +
cbProvName +
sizeof(WCHAR);
// allocate the string
if( (pBlob->pbData = (BYTE *) MyCoTaskMemAlloc(pBlob->cbData)) == NULL) {
SetLastError(ERROR_OUTOFMEMORY);
return(FALSE);
}
// copy the strings
memset(pBlob->pbData, 0, pBlob->cbData);
memcpy(pBlob->pbData, pKeyProvInfo->pwszContainerName, cbContainer);
memcpy(&pBlob->pbData[cbContainer], wszKeySpec, cbKeySpec);
memcpy(&pBlob->pbData[cbContainer + cbKeySpec], wszProvType, cbProvType);
memcpy(&pBlob->pbData[cbContainer + cbKeySpec + cbProvType], pKeyProvInfo->pwszProvName, cbProvName);
return(TRUE);
}
static LPWSTR wszEmpty = L"";
static LPWSTR wszMY = L"MY";
static LPWSTR wszCA = L"CA";
static LPWSTR wszROOT = L"ROOT";
static LPWSTR wszREQUEST = L"REQUEST";
static LPSTR szSystemStore = sz_CERT_STORE_PROV_SYSTEM;
// static LPSTR szSystemStore = sz_CERT_STORE_PROV_SYSTEM_REGISTRY;
/////////////////////////////////////////////////////////////////////////////
// CCEnroll
CCEnroll::~CCEnroll(void) {
Destruct();
DeleteCriticalSection(&m_csXEnroll);
}
void CCEnroll::Destruct(void) {
if(NULL != m_PrivateKeyArchiveCertificate)
{
CertFreeCertificateContext(m_PrivateKeyArchiveCertificate);
}
if(NULL != m_pCertContextSigner)
{
CertFreeCertificateContext(m_pCertContextSigner);
}
if(m_pCertContextRenewal != NULL)
CertFreeCertificateContext(m_pCertContextRenewal);
if(m_pCertContextStatic != NULL)
CertFreeCertificateContext(m_pCertContextStatic);
if(m_keyProvInfo.pwszContainerName != wszEmpty)
MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
if(m_keyProvInfo.pwszProvName != wszEmpty)
MyCoTaskMemFree(m_keyProvInfo.pwszProvName);
if(m_MyStore.wszName != wszMY)
MyCoTaskMemFree(m_MyStore.wszName);
if(m_CAStore.wszName != wszCA)
MyCoTaskMemFree(m_CAStore.wszName);
if(m_RootStore.wszName != wszROOT && m_RootStore.wszName != wszCA)
MyCoTaskMemFree(m_RootStore.wszName);
if(m_RequestStore.wszName != wszREQUEST)
MyCoTaskMemFree(m_RequestStore.wszName);
if(m_MyStore.szType != szSystemStore)
MyCoTaskMemFree(m_MyStore.szType);
if(m_CAStore.szType != szSystemStore)
MyCoTaskMemFree(m_CAStore.szType);
if(m_RootStore.szType != szSystemStore)
MyCoTaskMemFree(m_RootStore.szType);
if(m_RequestStore.szType != szSystemStore)
MyCoTaskMemFree(m_RequestStore.szType);
if(m_wszSPCFileName != wszEmpty)
MyCoTaskMemFree(m_wszSPCFileName);
if(m_wszPVKFileName != wszEmpty)
MyCoTaskMemFree(m_wszPVKFileName);
if (NULL != m_pCertContextPendingRequest)
CertFreeCertificateContext(m_pCertContextPendingRequest);
if (NULL != m_pPendingRequestTable)
delete m_pPendingRequestTable;
// store handles
if(m_RootStore.hStore != NULL)
CertCloseStore(m_RootStore.hStore, 0);
m_RootStore.hStore = NULL;
if(m_CAStore.hStore != NULL)
CertCloseStore(m_CAStore.hStore, 0);
m_CAStore.hStore = NULL;
if(m_MyStore.hStore != NULL)
CertCloseStore(m_MyStore.hStore, 0);
m_MyStore.hStore = NULL;
if(m_RequestStore.hStore != NULL)
CertCloseStore(m_RequestStore.hStore, 0);
m_RequestStore.hStore = NULL;
// remove provider handles
if(m_hProv != NULL)
CryptReleaseContext(m_hProv, 0);
m_hProv = NULL;
if(m_hVerifyProv != NULL)
CryptReleaseContext(m_hVerifyProv, 0);
m_hVerifyProv = NULL;
if (NULL != m_hCachedKey)
{
//this should be destroyed early but just in case
CryptDestroyKey(m_hCachedKey);
}
if (NULL != m_pPublicKeyInfo)
{
LocalFree(m_pPublicKeyInfo);
m_pPublicKeyInfo = NULL;
}
FreeAllStackExtension();
FreeAllStackAttribute();
resetBlobProperties();
}
static LPVOID CoTaskMemAllocTrap(ULONG cb) {
__try {
return(CoTaskMemAlloc(cb));
} __except(EXCEPTION_EXECUTE_HANDLER) {
SetLastError(ERROR_DLL_NOT_FOUND);
return(NULL);
}
}
static LPVOID CoTaskMemReallocTrap(LPVOID ptr, ULONG cb) {
__try {
return(CoTaskMemRealloc(ptr, cb));
} __except(EXCEPTION_EXECUTE_HANDLER) {
SetLastError(ERROR_DLL_NOT_FOUND);
return(NULL);
}
}
static void CoTaskMemFreeTrap(LPVOID ptr) {
__try {
CoTaskMemFree(ptr);
} __except(EXCEPTION_EXECUTE_HANDLER) {
SetLastError(ERROR_DLL_NOT_FOUND);
}
return;
}
// Initialize the safety options in the constructor, so they won't
// be clobbered when we do a reset().
CCEnroll::CCEnroll(void) : m_dwEnabledSafteyOptions(0) {
__try
{
InitializeCriticalSection(&m_csXEnroll);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
Init();
}
HRESULT
CCEnroll::Init(void)
{
HRESULT hr;
GUID guidContainerName;
char * sz = NULL;
RPC_STATUS rpc_status;
// set default mem allocators
if(MyCoTaskMemAlloc == NULL)
{
MyCoTaskMemAlloc = CoTaskMemAllocTrap;
MyCoTaskMemRealloc = CoTaskMemReallocTrap;
MyCoTaskMemFree = CoTaskMemFreeTrap;
}
// get a container based on a guid
rpc_status = UuidCreate(&guidContainerName);
if (RPC_S_OK != rpc_status && RPC_S_UUID_LOCAL_ONLY != rpc_status)
{
hr = rpc_status;
goto UuidCreateError;
}
rpc_status = UuidToStringA(&guidContainerName, (unsigned char **) &sz);
if (RPC_S_OK != rpc_status)
{
hr = rpc_status;
goto UuidToStringAError;
}
assert(sz != NULL);
m_keyProvInfo.pwszContainerName = WideFromMB(sz);
RpcStringFree((unsigned char **) &sz);
m_keyProvInfo.pwszProvName = wszEmpty;
m_keyProvInfo.dwProvType = PROV_RSA_FULL;
m_keyProvInfo.dwFlags = 0;
m_keyProvInfo.cProvParam = 0;
m_keyProvInfo.rgProvParam = NULL;
m_keyProvInfo.dwKeySpec = AT_SIGNATURE;
m_fEnableSMIMECapabilities =
(m_keyProvInfo.dwKeySpec == AT_KEYEXCHANGE);
m_fSMIMESetByClient = FALSE;
m_fKeySpecSetByClient = FALSE;
m_hProv = NULL;
m_hVerifyProv = NULL;
m_fDeleteRequestCert = TRUE;
m_fUseExistingKey = FALSE;
m_fWriteCertToCSPModified = FALSE;
m_fWriteCertToCSP = TRUE; // always want to try
m_fWriteCertToUserDSModified = FALSE;
m_fWriteCertToUserDS = FALSE;
m_fReuseHardwareKeyIfUnableToGenNew = TRUE;
m_fLimitExchangeKeyToEncipherment = FALSE;
m_dwT61DNEncoding = 0; // or CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG
m_dwGenKeyFlags = 0;
m_wszSPCFileName = wszEmpty;
m_wszPVKFileName = wszEmpty;
m_HashAlgId = 0;
m_fMyStoreOpenFlagsModified = FALSE;
m_MyStore.wszName = wszMY;
m_MyStore.szType = szSystemStore;
m_MyStore.dwFlags = CERT_SYSTEM_STORE_CURRENT_USER;
m_MyStore.hStore = NULL;
m_fCAStoreOpenFlagsModified = FALSE;
m_CAStore.wszName = wszCA;
m_CAStore.szType = szSystemStore;
m_CAStore.dwFlags = CERT_SYSTEM_STORE_CURRENT_USER;
m_CAStore.hStore = NULL;
m_fRootStoreOpenFlagsModified = FALSE;
m_RootStore.wszName = wszROOT;
m_RootStore.szType = szSystemStore;
m_RootStore.dwFlags = CERT_SYSTEM_STORE_CURRENT_USER;
m_RootStore.hStore = NULL;
m_fRequestStoreOpenFlagsModified = FALSE;
m_RequestStore.wszName = wszREQUEST ;
m_RequestStore.szType = szSystemStore;
m_RequestStore.dwFlags = CERT_SYSTEM_STORE_CURRENT_USER;
m_RequestStore.hStore = NULL;
m_PrivateKeyArchiveCertificate= NULL;
m_pCertContextRenewal = NULL;
m_pCertContextSigner = NULL;
m_pCertContextStatic = NULL;
memset(m_arHashBytesNewCert, 0, sizeof(m_arHashBytesNewCert));
memset(m_arHashBytesOldCert, 0, sizeof(m_arHashBytesOldCert));
m_fArchiveOldCert = FALSE;
m_pExtStack = NULL;
m_cExtStack = 0;
m_pAttrStack = NULL;
m_cAttrStack = 0;
m_pExtStackNew = NULL;
m_cExtStackNew = 0;
m_pAttrStackNew = NULL;
m_cAttrStackNew = 0;
m_pPropStack = NULL;
m_cPropStack = 0;
m_fNewRequestMethod = FALSE;
m_fCMCFormat = FALSE;
m_fHonorRenew = TRUE; //critical, if passing XECR_PKCS10*
m_fOID_V2 = FALSE; //critical
m_hCachedKey = NULL;
m_fUseClientKeyUsage = FALSE;
m_lClientId = XECI_XENROLL;
m_dwLastAlgIndex = MAXDWORD;
m_fIncludeSubjectKeyID = TRUE;
m_fHonorIncludeSubjectKeyID = FALSE;
m_pPublicKeyInfo = NULL;
m_dwSigKeyLenMax = 0;
m_dwSigKeyLenMin = 0;
m_dwSigKeyLenDef = 0;
m_dwSigKeyLenInc = 0;
m_dwXhgKeyLenMax = 0;
m_dwXhgKeyLenMin = 0;
m_dwXhgKeyLenDef = 0;
m_dwXhgKeyLenInc = 0;
// Initialize pending info data:
m_pCertContextPendingRequest = NULL;
m_pCertContextLastEnumerated = NULL;
m_dwCurrentPendingRequestIndex = 0;
m_pPendingRequestTable = NULL;
memset(&m_hashBlobPendingRequest, 0, sizeof(CRYPT_DATA_BLOB));
ZeroMemory(&m_blobResponseKAHash, sizeof(m_blobResponseKAHash));
hr = S_OK;
ErrorReturn:
return hr;
TRACE_ERROR(UuidToStringAError)
TRACE_ERROR(UuidCreateError)
}
void CCEnroll::FlushStore(StoreType storeType) {
PSTOREINFO pStoreInfo = NULL;
// get store struct
switch(storeType) {
case StoreMY:
pStoreInfo = &m_MyStore;
break;
case StoreCA:
pStoreInfo = &m_CAStore;
break;
case StoreROOT:
pStoreInfo = &m_RootStore;
break;
case StoreREQUEST:
pStoreInfo = &m_RequestStore;
break;
}
EnterCriticalSection(&m_csXEnroll);
// if store already open, return it
if(pStoreInfo->hStore != NULL) {
CertCloseStore(pStoreInfo->hStore, 0);
pStoreInfo->hStore = NULL;
}
// we may have something or not, but return it
// the errors will be correct.
LeaveCriticalSection(&m_csXEnroll);
}
HCERTSTORE CCEnroll::GetStore(StoreType storeType) {
PSTOREINFO pStoreInfo = NULL;
HCERTSTORE hStore = NULL;
// get store struct
switch(storeType) {
case StoreMY:
pStoreInfo = &m_MyStore;
break;
case StoreCA:
pStoreInfo = &m_CAStore;
break;
case StoreROOT:
pStoreInfo = &m_RootStore;
break;
case StoreREQUEST:
pStoreInfo = &m_RequestStore;
break;
default:
SetLastError(ERROR_BAD_ARGUMENTS);
return(NULL);
break;
}
EnterCriticalSection(&m_csXEnroll);
// if store already open, return it
if(pStoreInfo->hStore == NULL) {
// otherwise attempt to open the store
pStoreInfo->hStore = CertOpenStore(
pStoreInfo->szType,
PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
NULL,
pStoreInfo->dwFlags,
pStoreInfo->wszName);
}
// we may have something or not, but return it
// the errors will be correct.
hStore = pStoreInfo->hStore;
LeaveCriticalSection(&m_csXEnroll);
return(hStore);
}
HCRYPTPROV CCEnroll::GetProv(DWORD dwFlags) {
HCRYPTPROV hProvT = NULL;
DWORD cb = 0;
char * pszProvName = NULL;
char * pszContainerName = NULL;
EnterCriticalSection(&m_csXEnroll);
DWORD dwProvType = m_keyProvInfo.dwProvType;
switch(dwFlags) {
case CRYPT_NEWKEYSET:
dwFlags = dwFlags | m_keyProvInfo.dwFlags;
break;
case CRYPT_DELETEKEYSET:
if( m_hProv != NULL ) {
CryptReleaseContext(m_hProv, 0);
m_hProv = NULL;
CryptAcquireContextU(&m_hProv,
m_keyProvInfo.pwszContainerName,
m_keyProvInfo.pwszProvName,
m_keyProvInfo.dwProvType,
CRYPT_DELETEKEYSET);
}
m_hProv = NULL;
goto CommonReturn;
break;
default:
dwFlags = m_keyProvInfo.dwFlags;
break;
}
if(m_hProv == NULL) {
if( CryptAcquireContextU(&m_hProv,
m_keyProvInfo.pwszContainerName,
m_keyProvInfo.pwszProvName,
m_keyProvInfo.dwProvType,
dwFlags) ) {
// we have the m_hProv, now set the provider name
// Since this is secondary to the task, don't do error checking
// nothing here should really fail anyway
pszProvName = NULL;
while (TRUE)
{
if(!CryptGetProvParam( m_hProv,
PP_NAME,
(BYTE*)pszProvName,
&cb,
0))
{
break;
}
if (NULL != pszProvName)
{
if(m_keyProvInfo.pwszProvName != wszEmpty)
MyCoTaskMemFree(m_keyProvInfo.pwszProvName);
m_keyProvInfo.pwszProvName = WideFromMB(pszProvName);
break;
}
pszProvName = (char *)LocalAlloc(LMEM_FIXED, cb);
if (NULL == pszProvName)
{
goto CommonReturn;
}
}
// Here we just try and get the unique container name
// If not, just go on
BOOL fTryAnother = FALSE;
cb = 0;
pszContainerName = NULL;
while (TRUE)
{
if(!CryptGetProvParam( m_hProv,
PP_UNIQUE_CONTAINER,
(BYTE*)pszContainerName,
&cb,
0))
{
if (NULL == pszContainerName)
{
fTryAnother = TRUE;
}
else
{
LocalFree(pszContainerName);
pszContainerName = NULL;
}
break;
}
else
{
if (NULL != pszContainerName)
{
//got it, done
break;
}
}
pszContainerName = (char *)LocalAlloc(LMEM_FIXED, cb);
if (NULL == pszContainerName)
{
goto CommonReturn;
}
}
if (fTryAnother)
{
// so we can't get the unique container name,
// lets just go for the container name (may not be unique).
cb = 0;
pszContainerName = NULL;
while (TRUE)
{
if(!CryptGetProvParam(m_hProv,
PP_CONTAINER,
(BYTE*)pszContainerName,
&cb,
0))
{
if (NULL != pszContainerName)
{
LocalFree(pszContainerName);
pszContainerName = NULL;
}
break;
}
else
{
if (NULL != pszContainerName)
{
//got it, done
break;
}
pszContainerName = (char *)LocalAlloc(LMEM_FIXED, cb);
if (NULL == pszContainerName)
{
goto CommonReturn;
}
}
}
}
// set the container, otherwise use what was there
if(pszContainerName != NULL) {
if( m_keyProvInfo.pwszContainerName != wszEmpty )
MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
m_keyProvInfo.pwszContainerName = WideFromMB(pszContainerName);
}
// now because some providers double duty for provider types
// get what the the provider thinks its type is
cb = sizeof(DWORD);
if(CryptGetProvParam( m_hProv,
PP_PROVTYPE,
(BYTE *) &dwProvType,
&cb,
0) ) {
m_keyProvInfo.dwProvType = dwProvType;
}
} else {
m_hProv = NULL;
}
}
CommonReturn:
hProvT = m_hProv;
LeaveCriticalSection(&m_csXEnroll);
if (NULL != pszProvName)
{
LocalFree(pszProvName);
}
if (NULL != pszContainerName)
{
LocalFree(pszContainerName);
}
return(hProvT);
}
BOOL CCEnroll::SetKeyParams(
PCRYPT_KEY_PROV_INFO pKeyProvInfo
) {
EnterCriticalSection(&m_csXEnroll);
// remove provider handles
if(m_hProv != NULL)
CryptReleaseContext(m_hProv, 0);
m_hProv = NULL;
if(m_hVerifyProv != NULL)
CryptReleaseContext(m_hVerifyProv, 0);
m_hVerifyProv = NULL;
put_ContainerNameWStr(pKeyProvInfo->pwszContainerName);
put_ProviderNameWStr(pKeyProvInfo->pwszProvName);
put_ProviderFlags(pKeyProvInfo->dwFlags);
put_KeySpec(pKeyProvInfo->dwKeySpec);
put_ProviderType(pKeyProvInfo->dwProvType);
// someday we will have to pay attention to this too.
m_keyProvInfo.cProvParam = 0;
m_keyProvInfo.rgProvParam = NULL;
LeaveCriticalSection(&m_csXEnroll);
return(TRUE);
}
HRESULT STDMETHODCALLTYPE CCEnroll::createPKCS10(
/* [in] */ BSTR DNName,
/* [in] */ BSTR wszPurpose,
/* [retval][out] */ BSTR __RPC_FAR *pPKCS10) {
return(createPKCS10WStrBStr(DNName, wszPurpose, pPKCS10));
}
HRESULT CCEnroll::createPKCS10WStrBStr(
LPCWSTR DNName,
LPCWSTR wszPurpose,
BSTR __RPC_FAR *pPKCS10) {
HRESULT hr = S_OK;
CRYPT_DATA_BLOB blobPKCS10;
memset(&blobPKCS10, 0, sizeof(CRYPT_DATA_BLOB));
hr = createPKCS10WStr(DNName, wszPurpose, &blobPKCS10);
if(S_OK != hr)
{
goto createPKCS10Error;
}
// BASE64 encode pkcs 10, no header for backward compatible
hr = BlobToBstring(&blobPKCS10, CRYPT_STRING_BASE64, pPKCS10);
if (S_OK != hr)
{
goto BlobToBstringError;
}
CommonReturn:
if(NULL != blobPKCS10.pbData)
{
MyCoTaskMemFree(blobPKCS10.pbData);
}
return(hr);
ErrorReturn:
if(*pPKCS10 != NULL)
SysFreeString(*pPKCS10);
*pPKCS10 = NULL;
goto CommonReturn;
TRACE_ERROR(createPKCS10Error);
TRACE_ERROR(BlobToBstringError);
}
HRESULT CCEnroll::AddCertsToStores(
HCERTSTORE hStoreMsg,
LONG *plCertInstalled
) {
HCERTSTORE hStoreRoot = NULL;
HCERTSTORE hStoreCA = NULL;
PCCERT_CONTEXT pCertContext = NULL;
PCCERT_CONTEXT pCertContextLast = NULL;
LONG lCertInstalled = 0;
HRESULT hr = S_OK;
//init
if (NULL != plCertInstalled)
{
*plCertInstalled = 0;
}
EnterCriticalSection(&m_csXEnroll);
if( (hStoreCA = GetStore(StoreCA)) == NULL )
goto ErrorCertOpenCAStore;
if( (hStoreRoot = GetStore(StoreROOT)) == NULL )
goto ErrorCertOpenRootStore;
// now just place the rest of the cert in either the ROOT or CA store
// we know we removed the end-entity cert from the msg store already
// put all certs that came in the message into the appropriate store
while( (pCertContext = CertEnumCertificatesInStore(
hStoreMsg,
pCertContextLast)) != NULL ) {
// if it is a self sign, it is a ROOT
if( CertCompareCertificateName(
CRYPT_ASN_ENCODING,
&pCertContext->pCertInfo->Subject,
&pCertContext->pCertInfo->Issuer) ) {
// to root store could invoke a pop up, check cancel button
// but don't error out from any fail
if (MySafeCertAddCertificateContextToStore(
hStoreRoot,
pCertContext,
CERT_STORE_ADD_USE_EXISTING,
NULL,
m_dwEnabledSafteyOptions))
{
++lCertInstalled;
}
else
{
if (S_OK == hr)
{
//save the 1st error as return
hr = MY_HRESULT_FROM_WIN32(GetLastError());
if (HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED) == hr)
{
//map generic access deny to xenroll error
hr = XENROLL_E_CANNOT_ADD_ROOT_CERT;
}
}
//don't goto error here and finish the loop
}
}
// if it is not the MY cert, it must go in the CA store
// do nothing with the MY cert as we already handled it
else {
// likewise we don't care if these get added to the
// CA store
if (MySafeCertAddCertificateContextToStore(
hStoreCA,
pCertContext,
CERT_STORE_ADD_USE_EXISTING,
NULL,
m_dwEnabledSafteyOptions))
{
//no error code check
++lCertInstalled;
}
}
pCertContextLast = pCertContext;
}
pCertContextLast = NULL;
if (NULL != plCertInstalled)
{
*plCertInstalled = lCertInstalled;
}
CommonReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
ErrorReturn:
hr = MY_HRESULT_FROM_WIN32(GetLastError());
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
goto CommonReturn;
TRACE_ERROR(ErrorCertOpenCAStore);
TRACE_ERROR(ErrorCertOpenRootStore);
}
BOOL
IsDesiredProperty(DWORD dwPropertyId)
{
DWORD DesiredIds[] = {
CERT_PVK_FILE_PROP_ID,
CERT_FRIENDLY_NAME_PROP_ID,
CERT_DESCRIPTION_PROP_ID,
CERT_RENEWAL_PROP_ID,
};
DWORD i;
for (i = 0; i < ARRAYSIZE(DesiredIds); ++i)
{
if (dwPropertyId == DesiredIds[i])
{
return TRUE;
}
}
return FALSE;
}
BOOL
IsFilteredOutProperty(DWORD dwPropertyId)
{
DWORD FilteredIds[] = {
XENROLL_RENEWAL_CERTIFICATE_PROP_ID,
XENROLL_PASS_THRU_PROP_ID,
CERT_KEY_PROV_INFO_PROP_ID,
CERT_ENROLLMENT_PROP_ID, //pending property
};
DWORD i;
for (i = 0; i < ARRAYSIZE(FilteredIds); ++i)
{
if (dwPropertyId == FilteredIds[i])
{
return TRUE;
}
}
return FALSE;
}
HRESULT CCEnroll::GetEndEntityCert(
PCRYPT_DATA_BLOB pBlobPKCS7,
BOOL fSaveToStores,
PCCERT_CONTEXT *ppCert
)
{
HRESULT hr = S_OK;
HCERTSTORE hStoreMsg = NULL;
HCERTSTORE hStoreMy = NULL;
HCERTSTORE hStoreRequest = NULL;
PCCERT_CONTEXT pCertContextLast = NULL;
PCCERT_CONTEXT pCertContextRequest = NULL;
PCCERT_CONTEXT pCertContextMsg = NULL;
PCCERT_CONTEXT pCertContextArchive = NULL;
PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL;
DWORD cb = 0;
CRYPT_DATA_BLOB blobData;
CRYPT_HASH_BLOB blobHash = {sizeof(m_arHashBytesNewCert), m_arHashBytesNewCert};
CRYPT_HASH_BLOB blobHashRenew = {sizeof(m_arHashBytesOldCert), m_arHashBytesOldCert};
RequestFlags requestFlags;
CRYPT_HASH_BLOB requestFlagsBlob;
CRYPT_HASH_BLOB renewalCertBlob;
//Bug #202557 for IE3.02 upd clients (xiaohs)
HCRYPTPROV hProv=NULL;
BOOL fSetting;
DWORD dwPropertyId;
CRYPT_DATA_BLOB blobProp;
BYTE *pbArchivedKeyHash = NULL;
DWORD cbArchivedKeyHash = 0;
EnterCriticalSection(&m_csXEnroll);
memset(&requestFlags, 0, sizeof(RequestFlags));
memset(&blobData, 0, sizeof(CRYPT_DATA_BLOB));
memset(&requestFlagsBlob, 0, sizeof(CRYPT_DATA_BLOB));
memset(&renewalCertBlob, 0, sizeof(CRYPT_DATA_BLOB));
ZeroMemory(&blobProp, sizeof(blobProp));
if (NULL == ppCert)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto InvalidParameterError;
}
//init return
*ppCert = NULL;
if(!MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
pBlobPKCS7,
(CERT_QUERY_CONTENT_FLAG_CERT |
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED) ,
CERT_QUERY_FORMAT_FLAG_ALL,
0,
NULL,
NULL,
NULL,
&hStoreMsg,
NULL,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCryptQueryObject;
}
// check to see if this hash is in the message
if (m_pCertContextStatic == NULL ||
(NULL == (pCertContextMsg = CertFindCertificateInStore(
hStoreMsg,
X509_ASN_ENCODING,
0,
CERT_FIND_HASH,
&blobHash,
NULL))))
{
// open the request store
if (NULL == (hStoreRequest = GetStore(StoreREQUEST)))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCertOpenRequestStore;
}
// find cert in request store that matches cert
// in message, by public key
while (NULL != (pCertContextMsg = CertEnumCertificatesInStore(
hStoreMsg,
pCertContextLast)))
{
// check to see if this is in the request store
if (NULL != (pCertContextRequest = CertFindCertificateInStore(
hStoreRequest,
CRYPT_ASN_ENCODING,
0,
CERT_FIND_PUBLIC_KEY,
(void *) &pCertContextMsg->pCertInfo->SubjectPublicKeyInfo,
NULL)))
{
// found a match, get out
break;
}
pCertContextLast = pCertContextMsg;
}
pCertContextLast = NULL;
// if we didn't find one, then GetLastError was set either
// by CertEnumCerificatesInStore or CertEnumCerificatesInStore
if (NULL == pCertContextRequest)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorNoCertFound;
}
if (fSaveToStores)
{
// check archived key hash property first
// if the property exists, means key archival was in the request
cb = 0;
while (TRUE)
{
if(!CertGetCertificateContextProperty(
pCertContextRequest,
CERT_ARCHIVED_KEY_HASH_PROP_ID,
pbArchivedKeyHash,
&cbArchivedKeyHash))
{
if (NULL == pbArchivedKeyHash)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
if (MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND) == hr)
{
//no such property, so we are done
break;
}
// some other error
goto ErrorCertGetCertificateContextProperty;
}
else
{
//if pbArchivedKeyHash non-null, error
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCertGetCertificateContextProperty;
}
}
if (NULL != pbArchivedKeyHash)
{
//got it, done
break;
}
pbArchivedKeyHash = (BYTE*)LocalAlloc(
LMEM_FIXED, cbArchivedKeyHash);
if (NULL == pbArchivedKeyHash)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
if (NULL != pbArchivedKeyHash && NULL == m_blobResponseKAHash.pbData)
{
//request cert has archived key hash but response
//doesn't contain key hash for verification. maybe
//a spoofing response?
hr = XENROLL_E_RESPONSE_KA_HASH_NOT_FOUND;
goto ResponseKAHashNotFoundError;
}
if (NULL == pbArchivedKeyHash && NULL != m_blobResponseKAHash.pbData)
{
//request cert doesn't have archived key hash but
//response does. confliciting. seems no security harm
hr = XENROLL_E_RESPONSE_UNEXPECTED_KA_HASH;
goto ResponseUnexpectedKAHashError;
}
if (NULL != pbArchivedKeyHash && NULL != m_blobResponseKAHash.pbData)
{
//now we should check if they match
//compare size and hash
if (cbArchivedKeyHash != m_blobResponseKAHash.cbData ||
0 != memcmp(pbArchivedKeyHash,
m_blobResponseKAHash.pbData,
cbArchivedKeyHash))
{
//oh, potential attack
hr = XENROLL_E_RESPONSE_KA_HASH_MISMATCH;
//should remove the request cert?
goto ResponseKAMismatchError;
}
}
}
// get those request cert properties that are,
// either the property not blob property
// or blob property needs special handling
// Important: remember to add these Ids in IsFilteredOutProperty
fSetting = TRUE;
cb = 0;
while (TRUE)
{
if(!CertGetCertificateContextProperty(
pCertContextRequest,
CERT_KEY_PROV_INFO_PROP_ID,
pKeyProvInfo,
&cb))
{
if (NULL == pKeyProvInfo)
{
//skip setting
fSetting = FALSE;
break;
}
else
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCertGetCertificateContextProperty;
}
}
if (NULL != pKeyProvInfo)
{
//got it, done
break;
}
pKeyProvInfo = (PCRYPT_KEY_PROV_INFO)LocalAlloc(LMEM_FIXED, cb);
if (NULL == pKeyProvInfo)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
if (fSetting)
{
// put the property on the returned cert
if( !CertSetCertificateContextProperty(
pCertContextMsg,
CERT_KEY_PROV_INFO_PROP_ID,
0,
pKeyProvInfo) )
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorSetMyCertPropError;
}
// Set the provider info
SetKeyParams(pKeyProvInfo);
}
fSetting = TRUE;
while (TRUE)
{
if(!CertGetCertificateContextProperty(
pCertContextRequest,
XENROLL_PASS_THRU_PROP_ID,
requestFlagsBlob.pbData,
&requestFlagsBlob.cbData) )
{
if (NULL == requestFlagsBlob.pbData)
{
//do nothing
fSetting = FALSE;
break;
}
else
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorDecodeRequestFlags;
}
}
if (NULL != requestFlagsBlob.pbData)
{
//got it, done
break;
}
requestFlagsBlob.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
requestFlagsBlob.cbData);
if (NULL == requestFlagsBlob.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
if (fSetting)
{
// get the encoded blob
cb = sizeof(requestFlags);
// since this is a private data structure, its size should be
// known and this should aways pass
if (!CryptDecodeObject(
CRYPT_ASN_ENCODING,
XENROLL_REQUEST_INFO,
requestFlagsBlob.pbData,
requestFlagsBlob.cbData,
0,
&requestFlags,
&cb))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorDecodeRequestFlags;
}
// now set the flags
if(!m_fWriteCertToCSPModified)
m_fWriteCertToCSP = requestFlags.fWriteToCSP;
if(!m_fWriteCertToUserDSModified)
m_fWriteCertToUserDS = requestFlags.fWriteToDS;
if(!m_fRequestStoreOpenFlagsModified)
m_RequestStore.dwFlags = requestFlags.openFlags;
if(!m_fMyStoreOpenFlagsModified)
m_MyStore.dwFlags = (m_MyStore.dwFlags & ~CERT_SYSTEM_STORE_LOCATION_MASK) |
(requestFlags.openFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
if(!m_fCAStoreOpenFlagsModified)
m_CAStore.dwFlags = (m_CAStore.dwFlags & ~CERT_SYSTEM_STORE_LOCATION_MASK) |
(requestFlags.openFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
if(!m_fRootStoreOpenFlagsModified) {
//
// POTENTIAL SCRIPTING VIOLATION: we're mapping request store flags directly to root store flags.
// If they have set request store flags to local machine, propagate this setting to the root store flags,
// but set the root store name to "CA".
//
if (0 != m_dwEnabledSafteyOptions) {
if (requestFlags.openFlags & CERT_SYSTEM_STORE_LOCAL_MACHINE) {
m_RootStore.wszName = wszCA;
}
}
m_RootStore.dwFlags = (m_RootStore.dwFlags & ~CERT_SYSTEM_STORE_LOCATION_MASK) |
(requestFlags.openFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
}
}
// see if this is a renewal request
m_fArchiveOldCert = FALSE;
fSetting = TRUE;
while (TRUE)
{
// get the encoded blob
if (!CertGetCertificateContextProperty(
pCertContextRequest,
XENROLL_RENEWAL_CERTIFICATE_PROP_ID,
renewalCertBlob.pbData,
&renewalCertBlob.cbData))
{
if (NULL == renewalCertBlob.pbData)
{
fSetting = FALSE;
break;
}
else
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCertGetCertificateContextProperty;
}
}
if (NULL != renewalCertBlob.pbData)
{
//got it, done
break;
}
renewalCertBlob.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
renewalCertBlob.cbData);
if (NULL == renewalCertBlob.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
if (fSetting)
{
//Bug #202557 for IE3.02 upd clients (xiaohs)
if (NULL==hProv)
{
if(!CryptAcquireContext(
&hProv,
NULL,
MS_DEF_PROV,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorAcquireContext;
}
}
if (!CryptHashCertificate(
hProv, //NULL, Bug #202557 for IE3.02 upd clients (xiaohs)
0, //alg
X509_ASN_ENCODING, //0 dwFlags
renewalCertBlob.pbData,
renewalCertBlob.cbData,
blobHashRenew.pbData,
&blobHashRenew.cbData))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCryptHashCertificate;
}
m_fArchiveOldCert = TRUE;
}
//get rest of blob properties from request store and set to the cert
dwPropertyId = CertEnumCertificateContextProperties(
pCertContextRequest, 0); //enum from 1st
while (0 != dwPropertyId)
{
// if (!IsFilteredOutProperty(dwPropertyId))
//because iis cert install doesn't like to copy all properties from
//request cert to install cert we just copy selected properties for now
if (IsDesiredProperty(dwPropertyId))
{
fSetting = TRUE;
while (TRUE)
{
if (!CertGetCertificateContextProperty(
pCertContextRequest,
dwPropertyId,
blobProp.pbData,
&blobProp.cbData))
{
//no get, no set, go on
fSetting = FALSE;
break;
}
if (NULL != blobProp.pbData)
{
//done
break;
}
blobProp.pbData = (BYTE*)LocalAlloc(LMEM_FIXED,
blobProp.cbData);
if (NULL == blobProp.pbData)
{
goto OutOfMemoryError;
}
}
if (fSetting)
{
//should get the property from the request cert
if (!CertSetCertificateContextProperty(
pCertContextMsg,
dwPropertyId,
0,
&blobProp))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorSetMyCertPropError;
}
}
if (NULL != blobProp.pbData)
{
//set for the next enum
LocalFree(blobProp.pbData);
blobProp.pbData = NULL;
}
}
dwPropertyId = CertEnumCertificateContextProperties(
pCertContextRequest,
dwPropertyId);
}
// save this away in the cache
if(m_pCertContextStatic != NULL)
CertFreeCertificateContext(m_pCertContextStatic);
m_pCertContextStatic = CertDuplicateCertificateContext(pCertContextMsg);
//Bug #202557 for IE3.02 upd clients (xiaohs)
if(NULL==hProv)
{
if(!CryptAcquireContext(
&hProv,
NULL,
MS_DEF_PROV,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorAcquireContext;
}
}
if( !CryptHashCertificate(
hProv, //NULL Bug #202557 for IE3.02 upd clients (xiaohs)
0,
X509_ASN_ENCODING,
pCertContextMsg->pbCertEncoded,
pCertContextMsg->cbCertEncoded,
blobHash.pbData,
&blobHash.cbData) )
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCryptHashCertificate;
}
}
// at this point we have 2 context m_pCertContextStatic which we want to return to the user
// and pCertContextMsg which we want to delete from the Msg store
assert(pCertContextMsg != NULL);
CertDeleteCertificateFromStore(pCertContextMsg);
pCertContextMsg = NULL; // freed by the delete
// we want to return our static, so make a dup and this is what we will return
assert(m_pCertContextStatic != NULL);
pCertContextMsg = CertDuplicateCertificateContext(m_pCertContextStatic);
// put these in the stores if asked
if(fSaveToStores) {
// open the stores
if( (hStoreMy = GetStore(StoreMY)) == NULL)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCertOpenMYStore;
}
// we know that the pCertContextMsg is a dup of the end-entity cert in m_pCertContextStatic
// and we want to put this in the MY store
assert(pCertContextMsg != NULL);
if( !MySafeCertAddCertificateContextToStore(
hStoreMy,
pCertContextMsg,
CERT_STORE_ADD_USE_EXISTING,
NULL,
m_dwEnabledSafteyOptions) )
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCertAddToMyStore;
}
// If we have renewal, then mark the old cert as an archive
if(m_fArchiveOldCert &&
((pCertContextArchive = CertFindCertificateInStore(
hStoreMy,
X509_ASN_ENCODING,
0,
CERT_FIND_HASH,
&blobHashRenew,
NULL)) != NULL) ) {
// Set the Archive property on the cert.
// crypt32 in IE3.02upd does not support this prop, so don't fail on error
CertSetCertificateContextProperty(
pCertContextArchive,
CERT_ARCHIVED_PROP_ID,
0,
&blobData);
//set new cert hash on old archived cert
//ignore error if it fails
CertSetCertificateContextProperty(
pCertContextArchive,
CERT_RENEWAL_PROP_ID,
0,
&blobHash);
}
// add the rest of the certs to the stores
hr = AddCertsToStores(hStoreMsg, NULL);
//ignore cancel error since it from root cert install
//ignore XENROLL_E_CANNOT_ADD_ROOT_CERT also
if (S_OK != hr &&
MY_HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr &&
XENROLL_E_CANNOT_ADD_ROOT_CERT != hr)
{
goto ErrorAddCertsToStores;
}
}
*ppCert = pCertContextMsg;
CommonReturn:
//Bug #202557 for IE3.02 upd clients (xiaohs)
if(hProv)
CryptReleaseContext(hProv, 0);
if(pCertContextRequest != NULL)
CertFreeCertificateContext(pCertContextRequest);
if(pCertContextArchive != NULL)
CertFreeCertificateContext(pCertContextArchive);
// it really should be NULL
assert(pCertContextLast == NULL);
if(hStoreMsg != NULL)
CertCloseStore(hStoreMsg, 0);
// we need to do this because the store that may be opened is the systemstore, but
// the store we may need is the local machine store, but we don't know that until the
// system store finds the request cert in the local machine physical store.
// Later when we do the delete, we want the local machine store open.
FlushStore(StoreREQUEST);
if (NULL != requestFlagsBlob.pbData)
{
LocalFree(requestFlagsBlob.pbData);
}
if (NULL != renewalCertBlob.pbData)
{
LocalFree(renewalCertBlob.pbData);
}
if (NULL != blobProp.pbData)
{
LocalFree(blobProp.pbData);
}
if (NULL != pKeyProvInfo)
{
LocalFree(pKeyProvInfo);
}
if (NULL != pbArchivedKeyHash)
{
LocalFree(pbArchivedKeyHash);
}
LeaveCriticalSection(&m_csXEnroll);
return (hr);
ErrorReturn:
if(NULL != pCertContextMsg)
{
CertFreeCertificateContext(pCertContextMsg);
}
goto CommonReturn;
TRACE_ERROR(ErrorCryptHashCertificate);
TRACE_ERROR(ErrorCertOpenMYStore);
TRACE_ERROR(ErrorCertAddToMyStore);
TRACE_ERROR(ErrorCryptQueryObject);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorNoCertFound);
TRACE_ERROR(ErrorCertGetCertificateContextProperty);
TRACE_ERROR(ErrorSetMyCertPropError);
TRACE_ERROR(ErrorDecodeRequestFlags);
TRACE_ERROR(ErrorAcquireContext); //Bug #202557 for IE3.02 upd clients (xiaohs)
TRACE_ERROR(ErrorAddCertsToStores);
TRACE_ERROR(OutOfMemoryError);
TRACE_ERROR(InvalidParameterError);
TRACE_ERROR(ResponseKAMismatchError)
TRACE_ERROR(ResponseUnexpectedKAHashError)
TRACE_ERROR(ResponseKAHashNotFoundError)
}
HRESULT STDMETHODCALLTYPE CCEnroll::getCertFromPKCS7(
/* [in] */ BSTR wszPKCS7,
/* [retval][out] */ BSTR __RPC_FAR *pbstrCert
) {
HRESULT hr;
CRYPT_DATA_BLOB blobPKCS7;
CRYPT_DATA_BLOB blobX509;
PCCERT_CONTEXT pCertContextMy = NULL;
assert(wszPKCS7 != NULL && pbstrCert != NULL);
if (NULL == wszPKCS7 || NULL == pbstrCert)
goto PointerError;
// just put into a blob
memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
blobPKCS7.pbData = (PBYTE) wszPKCS7;
// Get a Cert Context for the end-entity
if( (pCertContextMy = getCertContextFromPKCS7(&blobPKCS7)) == NULL)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto getCertContextFromPKCS7Error;
}
blobX509.pbData = pCertContextMy->pbCertEncoded;
blobX509.cbData = pCertContextMy->cbCertEncoded;
//base64 with no header for backward compatible
hr = BlobToBstring(&blobX509, CRYPT_STRING_BASE64, pbstrCert);
if (S_OK != hr)
{
goto BlobToBstringError;
}
hr = S_OK;
ErrorReturn:
if(pCertContextMy != NULL)
CertFreeCertificateContext(pCertContextMy);
return(hr);
SET_HRESULT(PointerError, E_POINTER);
TRACE_ERROR(getCertContextFromPKCS7Error);
TRACE_ERROR(BlobToBstringError);
}
HRESULT STDMETHODCALLTYPE CCEnroll::acceptPKCS7(
/* [in] */ BSTR wszPKCS7) {
CRYPT_DATA_BLOB blobPKCS7;
HRESULT hr;
assert(wszPKCS7 != NULL);
if (NULL == wszPKCS7)
goto PointerError;
// just put into a blob
memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
blobPKCS7.pbData = (PBYTE) wszPKCS7;
// accept the blob
hr = acceptPKCS7Blob(&blobPKCS7);
ErrorReturn:
return hr;
SET_HRESULT(PointerError, E_POINTER);
}
HRESULT STDMETHODCALLTYPE CCEnroll::createFilePKCS10(
/* [in] */ BSTR DNName,
/* [in] */ BSTR Usage,
/* [in] */ BSTR wszPKCS10FileName) {
return(createFilePKCS10WStr(DNName, Usage, wszPKCS10FileName));
}
HRESULT STDMETHODCALLTYPE CCEnroll::addCertTypeToRequest(
/* [in] */ BSTR CertType) {
return(AddCertTypeToRequestWStr(CertType));
}
HRESULT STDMETHODCALLTYPE CCEnroll::addCertTypeToRequestEx(
IN LONG lType,
IN BSTR bstrOIDOrName,
IN LONG lMajorVersion,
IN BOOL fMinorVersion,
IN LONG lMinorVersion)
{
return AddCertTypeToRequestWStrEx(
lType,
bstrOIDOrName,
lMajorVersion,
fMinorVersion,
lMinorVersion);
}
HRESULT STDMETHODCALLTYPE CCEnroll::getProviderType(
IN BSTR strProvName,
OUT LONG *plProvType)
{
return getProviderTypeWStr(strProvName, plProvType);
}
HRESULT STDMETHODCALLTYPE CCEnroll::addNameValuePairToSignature(
/* [in] */ BSTR Name,
/* [in] */ BSTR Value) {
return(AddNameValuePairToSignatureWStr(Name, Value));
}
HRESULT STDMETHODCALLTYPE CCEnroll::acceptFilePKCS7(
/* [in] */ BSTR wszPKCS7FileName) {
return(acceptFilePKCS7WStr(wszPKCS7FileName));
}
HRESULT STDMETHODCALLTYPE CCEnroll::freeRequestInfo(
/* [in] */ BSTR bstrPKCS7OrPKCS10)
{
HRESULT hr;
CRYPT_DATA_BLOB blob;
BYTE *pbData = NULL;
DWORD cbData = 0;
// could be base64
while (TRUE)
{
if (!MyCryptStringToBinaryW(
(WCHAR*)bstrPKCS7OrPKCS10,
SysStringLen(bstrPKCS7OrPKCS10),
CRYPT_STRING_ANY,
pbData,
&cbData,
NULL,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto MyCryptStringToBinaryWError;
}
if (NULL != pbData)
{
break; //done
}
pbData = (BYTE*)LocalAlloc(LMEM_FIXED, cbData);
if (NULL == pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
blob.cbData = cbData;
blob.pbData = pbData;
hr = freeRequestInfoBlob(blob);
if (S_OK != hr)
{
goto freeRequestInfoBlobError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pbData)
{
LocalFree(pbData);
}
return hr;
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(freeRequestInfoBlobError)
}
//
// MY STORE
//
HCERTSTORE STDMETHODCALLTYPE CCEnroll::getMyStore( void)
{
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return NULL;
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreName(
/* [retval][out] */ BSTR __RPC_FAR *pbstrName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_MyStore.wszName == NULL)
return(ERROR_UNKNOWN_PROPERTY);
if( (*pbstrName = SysAllocString(m_MyStore.wszName)) == NULL )
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreName(
/* [in] */ BSTR bstrName) {
return(put_MyStoreNameWStr(bstrName));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreType(
/* [retval][out] */ BSTR __RPC_FAR *pbstrType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*pbstrType = BSTRFromMB(m_MyStore.szType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreType(
/* [in] */ BSTR bstrType) {
return(put_MyStoreTypeWStr(bstrType));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreFlags(
/* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
EnterCriticalSection(&m_csXEnroll);
*pdwFlags = m_MyStore.dwFlags;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe.
// See VerifyStoreFlagsSafeForScripting().
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreFlags(
/* [in] */ LONG dwFlags) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
// If we're marked as safe for scripting, see if the flags passed in are safe:
if (0 != m_dwEnabledSafteyOptions && !VerifyStoreFlagsSafeForScripting(dwFlags))
goto AccessDeniedError;
if(m_MyStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
// set the my store flags
m_MyStore.dwFlags = dwFlags;
m_fMyStoreOpenFlagsModified = TRUE;
m_keyProvInfo.dwFlags |= KeyLocationFromStoreLocation(dwFlags);
// track the request store location to the my store, only if the request store has not been modified
// do NOT set the modify bit for the request store, this is a default
if(!m_fRequestStoreOpenFlagsModified) {
m_RequestStore.dwFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
m_RequestStore.dwFlags |= (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
}
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreNameWStr(
/* [out] */ LPWSTR __RPC_FAR *szwName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_MyStore.wszName == NULL)
return(ERROR_UNKNOWN_PROPERTY);
if( (*szwName = CopyWideString(m_MyStore.wszName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreNameWStr(
/* [in] */ LPWSTR szwName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
goto AccessDeniedError;
if(m_MyStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_MyStore.wszName != wszMY)
MyCoTaskMemFree(m_MyStore.wszName);
if( (m_MyStore.wszName = CopyWideString(szwName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_MyStoreTypeWStr(
/* [out] */ LPWSTR __RPC_FAR *szwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*szwType = WideFromMB(m_MyStore.szType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_MyStoreTypeWStr(
/* [in] */ LPWSTR szwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
goto AccessDeniedError;
if(m_MyStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_MyStore.szType != szSystemStore)
MyCoTaskMemFree(m_MyStore.szType);
if( (m_MyStore.szType = MBFromWide(szwType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
//
// CA STORE
//
HCERTSTORE STDMETHODCALLTYPE CCEnroll::getCAStore( void)
{
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return NULL;
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreName(
/* [retval][out] */ BSTR __RPC_FAR *pbstrName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_CAStore.wszName == NULL)
return(ERROR_UNKNOWN_PROPERTY);
if( (*pbstrName = SysAllocString(m_CAStore.wszName)) == NULL )
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreName(
/* [in] */ BSTR bstrName) {
return(put_CAStoreNameWStr(bstrName));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreType(
/* [retval][out] */ BSTR __RPC_FAR *pbstrType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*pbstrType = BSTRFromMB(m_CAStore.szType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreType(
/* [in] */ BSTR bstrType) {
return(put_CAStoreTypeWStr(bstrType));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreFlags(
/* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
EnterCriticalSection(&m_csXEnroll);
*pdwFlags = m_CAStore.dwFlags;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe.
// See VerifyStoreFlagsSafeForScripting().
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreFlags(
/* [in] */ LONG dwFlags) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
// If we're marked as safe for scripting, see if the flags passed in are safe:
if (0 != m_dwEnabledSafteyOptions && !VerifyStoreFlagsSafeForScripting(dwFlags))
goto AccessDeniedError;
if(m_CAStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
m_fCAStoreOpenFlagsModified = TRUE;
m_CAStore.dwFlags = dwFlags;
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreNameWStr(
/* [out] */ LPWSTR __RPC_FAR *szwName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_CAStore.wszName == NULL)
return(ERROR_UNKNOWN_PROPERTY);
if( (*szwName = CopyWideString(m_CAStore.wszName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreNameWStr(
/* [in] */ LPWSTR szwName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
goto AccessDeniedError;
if(m_CAStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_CAStore.wszName != wszCA)
MyCoTaskMemFree(m_CAStore.wszName);
if( (m_CAStore.wszName = CopyWideString(szwName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_CAStoreTypeWStr(
/* [out] */ LPWSTR __RPC_FAR *szwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*szwType = WideFromMB(m_CAStore.szType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_CAStoreTypeWStr(
/* [in] */ LPWSTR szwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
goto AccessDeniedError;
if(m_CAStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_CAStore.szType != szSystemStore)
MyCoTaskMemFree(m_CAStore.szType);
if( (m_CAStore.szType = MBFromWide(szwType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
//
// ROOT STORE
//
HCERTSTORE STDMETHODCALLTYPE CCEnroll::getROOTHStore( void)
{
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return NULL;
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreName(
/* [retval][out] */ BSTR __RPC_FAR *pbstrName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_RootStore.wszName == NULL)
return(ERROR_UNKNOWN_PROPERTY);
if( (*pbstrName = SysAllocString(m_RootStore.wszName)) == NULL )
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreName(
/* [in] */ BSTR bstrName) {
return(put_RootStoreNameWStr(bstrName));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreType(
/* [retval][out] */ BSTR __RPC_FAR *pbstrType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*pbstrType = BSTRFromMB(m_RootStore.szType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreType(
/* [in] */ BSTR bstrType) {
return(put_RootStoreTypeWStr(bstrType));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreFlags(
/* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
EnterCriticalSection(&m_csXEnroll);
*pdwFlags = m_RootStore.dwFlags;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe.
// See VerifyStoreFlagsSafeForScripting().
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreFlags(
/* [in] */ LONG dwFlags) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
// If we're marked as safe for scripting, see if the flags passed in are safe:
if (0 != m_dwEnabledSafteyOptions) {
// see if the caller passed safe flags:
if (!VerifyStoreFlagsSafeForScripting(dwFlags))
goto AccessDeniedError;
// extra check for root store: don't allow CERT_SYSTEM_STORE_LOCAL_MACHINE for the root store.
// if they want to install a machine cert through script, it'll go to the CA store:
// NOTE: we don't modify the store name if m_RootStore.hStore is NULL, as we'll return E_ACCESSDENIED
// anyway, and we shouldn't modify this on error.
if (NULL == m_RootStore.hStore) {
if (dwFlags & CERT_SYSTEM_STORE_LOCAL_MACHINE) {
m_RootStore.wszName = wszCA;
} else {
m_RootStore.wszName = wszROOT;
}
}
}
if(m_RootStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
m_fRootStoreOpenFlagsModified = TRUE;
m_RootStore.dwFlags = dwFlags;
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreNameWStr(
/* [out] */ LPWSTR __RPC_FAR *szwName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_RootStore.wszName == NULL)
return(ERROR_UNKNOWN_PROPERTY);
if( (*szwName = CopyWideString(m_RootStore.wszName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreNameWStr(
/* [in] */ LPWSTR szwName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
goto AccessDeniedError;
if(m_RootStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_RootStore.wszName != wszROOT && m_RootStore.wszName != wszCA)
MyCoTaskMemFree(m_RootStore.wszName);
if( (m_RootStore.wszName = CopyWideString(szwName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RootStoreTypeWStr(
/* [out] */ LPWSTR __RPC_FAR *szwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*szwType = WideFromMB(m_RootStore.szType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RootStoreTypeWStr(
/* [in] */ LPWSTR szwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
goto AccessDeniedError;
if(m_RootStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_RootStore.szType != szSystemStore)
MyCoTaskMemFree(m_RootStore.szType);
if( (m_RootStore.szType = MBFromWide(szwType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
//
// REQUEST STORE
//
HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreName(
/* [retval][out] */ BSTR __RPC_FAR *pbstrName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_RequestStore.wszName == NULL)
return(ERROR_UNKNOWN_PROPERTY);
if( (*pbstrName = SysAllocString(m_RequestStore.wszName)) == NULL )
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreName(
/* [in] */ BSTR bstrName) {
return(put_RequestStoreNameWStr(bstrName));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreType(
/* [retval][out] */ BSTR __RPC_FAR *pbstrType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*pbstrType = BSTRFromMB(m_RequestStore.szType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreType(
/* [in] */ BSTR bstrType) {
return(put_RequestStoreTypeWStr(bstrType));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreFlags(
/* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
EnterCriticalSection(&m_csXEnroll);
*pdwFlags = m_RequestStore.dwFlags;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe.
// See VerifyStoreFlagsSafeForScripting().
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreFlags(
/* [in] */ LONG dwFlags) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
// If we're marked as safe for scripting, see if the flags passed in are safe:
if (0 != m_dwEnabledSafteyOptions && !VerifyStoreFlagsSafeForScripting(dwFlags))
goto AccessDeniedError;
if(m_RequestStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
// set the request store flags
m_RequestStore.dwFlags = dwFlags;
m_fRequestStoreOpenFlagsModified = TRUE;
// track the My store location to the request store, only if the my store has not been modified
// do NOT set the modify bit for the my store, this is a default
if(!m_fMyStoreOpenFlagsModified) {
m_MyStore.dwFlags &= ~CERT_SYSTEM_STORE_LOCATION_MASK;
m_MyStore.dwFlags |= (dwFlags & CERT_SYSTEM_STORE_LOCATION_MASK);
m_keyProvInfo.dwFlags |= KeyLocationFromStoreLocation(m_MyStore.dwFlags);
}
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreNameWStr(
/* [out] */ LPWSTR __RPC_FAR *szwName) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_RequestStore.wszName == NULL)
return(ERROR_UNKNOWN_PROPERTY);
if( (*szwName = CopyWideString(m_RequestStore.wszName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreNameWStr(
/* [in] */ LPWSTR szwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
goto AccessDeniedError;
if(m_RequestStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_RequestStore.wszName != wszREQUEST)
MyCoTaskMemFree(m_RequestStore.wszName);
if( (m_RequestStore.wszName = CopyWideString(szwType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RequestStoreTypeWStr(
/* [out] */ LPWSTR __RPC_FAR *szwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*szwType = WideFromMB(m_RequestStore.szType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_RequestStoreTypeWStr(
/* [in] */ LPWSTR szwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
goto AccessDeniedError;
if(m_RequestStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_RequestStore.szType != szSystemStore)
MyCoTaskMemFree(m_RequestStore.szType);
if( (m_RequestStore.szType = MBFromWide(szwType)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
//
// Provider Stuff
//
HRESULT STDMETHODCALLTYPE CCEnroll::get_ContainerName(
/* [retval][out] */ BSTR __RPC_FAR *pbstrContainer) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*pbstrContainer = SysAllocString(m_keyProvInfo.pwszContainerName)) == NULL )
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_ContainerName(
/* [in] */ BSTR bstrContainer) {
return(put_ContainerNameWStr(bstrContainer));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderName(
/* [retval][out] */ BSTR __RPC_FAR *pbstrProvider) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*pbstrProvider = SysAllocString(m_keyProvInfo.pwszProvName)) == NULL )
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderName(
/* [in] */ BSTR bstrProvider) {
return(put_ProviderNameWStr(bstrProvider));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderType(
/* [retval][out] */ LONG __RPC_FAR *pdwType) {
EnterCriticalSection(&m_csXEnroll);
*pdwType = m_keyProvInfo.dwProvType;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderType(
/* [in] */ LONG dwType) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_hProv != NULL)
hr = E_ACCESSDENIED;
else
m_keyProvInfo.dwProvType = dwType;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_KeySpec(
/* [retval][out] */ LONG __RPC_FAR *pdw) {
EnterCriticalSection(&m_csXEnroll);
*pdw = m_keyProvInfo.dwKeySpec;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_KeySpec(
/* [in] */ LONG dwKeySpec) {
HRESULT hr;
EnterCriticalSection(&m_csXEnroll);
if(m_hProv != NULL)
{
hr = E_ACCESSDENIED;
goto NullProvError;
}
if (m_fSMIMESetByClient)
{
//SMIME is set by the client
if (m_fEnableSMIMECapabilities && AT_SIGNATURE == dwKeySpec)
{
//try to set signature key spec also SMIME
hr = XENROLL_E_KEYSPEC_SMIME_MISMATCH;
goto MismatchError;
}
}
else
{
//currently smime is not set by user
//turn on SMIME for according to key spec
m_fEnableSMIMECapabilities = (dwKeySpec == AT_KEYEXCHANGE);
}
m_keyProvInfo.dwKeySpec = dwKeySpec;
m_fKeySpecSetByClient = TRUE;
hr = S_OK;
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
TRACE_ERROR(NullProvError)
TRACE_ERROR(MismatchError)
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_ClientId(
/* [retval][out] */ LONG __RPC_FAR *pdw) {
EnterCriticalSection(&m_csXEnroll);
*pdw = m_lClientId;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_ClientId(
/* [in] */ LONG dw) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
m_lClientId = dw;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_IncludeSubjectKeyID(
/* [retval][out] */ BOOL __RPC_FAR *pfInclude) {
EnterCriticalSection(&m_csXEnroll);
*pfInclude = m_fIncludeSubjectKeyID;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_IncludeSubjectKeyID(
/* [in] */ BOOL fInclude) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
m_fIncludeSubjectKeyID = fInclude;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderFlags(
/* [retval][out] */ LONG __RPC_FAR *pdwFlags) {
EnterCriticalSection(&m_csXEnroll);
*pdwFlags = m_keyProvInfo.dwFlags;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
//--------------------------------------------------------------------------------
//
// This method is only safe for scripting if it's parameter is safe.
// See VerifyProviderFlagsSafeForScripting().
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderFlags(
/* [in] */ LONG dwFlags) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
// If we're marked as safe for scripting, see if the flags passed in are safe:
if (0 != m_dwEnabledSafteyOptions && !VerifyProviderFlagsSafeForScripting(dwFlags))
goto AccessDeniedError;
if(m_hProv != NULL)
hr = E_ACCESSDENIED;
else
m_keyProvInfo.dwFlags = dwFlags;
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
SET_HRESULT(AccessDeniedError, E_ACCESSDENIED);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_ContainerNameWStr(
/* [out] */ LPWSTR __RPC_FAR *szwContainer) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*szwContainer = CopyWideString(m_keyProvInfo.pwszContainerName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_ContainerNameWStr(
/* [in] */ LPWSTR szwContainer) {
HRESULT hr = S_OK;
if(szwContainer == NULL)
return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
EnterCriticalSection(&m_csXEnroll);
if(m_hProv != NULL)
hr = E_ACCESSDENIED;
else {
if( m_keyProvInfo.pwszContainerName != wszEmpty)
MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
if( (m_keyProvInfo.pwszContainerName = CopyWideString(szwContainer)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_ProviderNameWStr(
/* [out] */ LPWSTR __RPC_FAR *szwProvider) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*szwProvider = CopyWideString(m_keyProvInfo.pwszProvName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_ProviderNameWStr(
/* [in] */ LPWSTR szwProvider) {
HRESULT hr = S_OK;
if(szwProvider == NULL)
return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
EnterCriticalSection(&m_csXEnroll);
if(m_hProv != NULL)
hr = E_ACCESSDENIED;
else {
if (0 != wcscmp(m_keyProvInfo.pwszProvName, szwProvider))
{
if( m_keyProvInfo.pwszProvName != wszEmpty )
MyCoTaskMemFree(m_keyProvInfo.pwszProvName);
if( (m_keyProvInfo.pwszProvName = CopyWideString(szwProvider)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
//one last thing, free/null cached prov handle
if (NULL != m_hVerifyProv)
{
CryptReleaseContext(m_hVerifyProv, 0);
m_hVerifyProv = NULL;
}
// csp is changed, reset key size cache
m_dwXhgKeyLenMax = 0;
m_dwXhgKeyLenMin = 0;
m_dwXhgKeyLenDef = 0;
m_dwXhgKeyLenInc = 0;
m_dwSigKeyLenMax = 0;
m_dwSigKeyLenMin = 0;
m_dwSigKeyLenDef = 0;
m_dwSigKeyLenInc = 0;
}
}
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
//
// Other Stuff
//
HRESULT STDMETHODCALLTYPE CCEnroll::get_UseExistingKeySet(
/* [retval][out] */ BOOL __RPC_FAR *fUseExistingKeys) {
EnterCriticalSection(&m_csXEnroll);
*fUseExistingKeys = m_fUseExistingKey;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_UseExistingKeySet(
/* [in] */ BOOL fUseExistingKeys) {
EnterCriticalSection(&m_csXEnroll);
m_fUseExistingKey = fUseExistingKeys;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_GenKeyFlags(
/* [retval][out] */ LONG __RPC_FAR * pdwFlags) {
EnterCriticalSection(&m_csXEnroll);
*pdwFlags = m_dwGenKeyFlags;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_GenKeyFlags(
/* [in] */ LONG dwFlags) {
EnterCriticalSection(&m_csXEnroll);
m_dwGenKeyFlags = dwFlags;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_DeleteRequestCert(
/* [retval][out] */ BOOL __RPC_FAR *fBool) {
EnterCriticalSection(&m_csXEnroll);
*fBool = m_fDeleteRequestCert;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_DeleteRequestCert(
/* [in] */ BOOL fBool) {
EnterCriticalSection(&m_csXEnroll);
m_fDeleteRequestCert = fBool;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_WriteCertToCSP(
/* [retval][out] */ BOOL __RPC_FAR *fBool) {
EnterCriticalSection(&m_csXEnroll);
*fBool = m_fWriteCertToCSP;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_WriteCertToCSP(
/* [in] */ BOOL fBool) {
EnterCriticalSection(&m_csXEnroll);
m_fWriteCertToCSP = fBool;
m_fWriteCertToCSPModified = TRUE;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_WriteCertToUserDS(
/* [retval][out] */ BOOL __RPC_FAR *fBool) {
EnterCriticalSection(&m_csXEnroll);
*fBool = m_fWriteCertToUserDS;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::put_WriteCertToUserDS(
/* [in] */ BOOL fBool) {
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
return E_ACCESSDENIED;
EnterCriticalSection(&m_csXEnroll);
m_fWriteCertToUserDS = fBool;
m_fWriteCertToUserDSModified = TRUE;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_EnableT61DNEncoding(
/* [retval][out] */ BOOL __RPC_FAR *fBool) {
EnterCriticalSection(&m_csXEnroll);
*fBool = (m_dwT61DNEncoding == CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG);
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_EnableT61DNEncoding(
/* [in] */ BOOL fBool) {
EnterCriticalSection(&m_csXEnroll);
if(fBool)
m_dwT61DNEncoding = CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG;
else
m_dwT61DNEncoding = 0;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_SPCFileName(
/* [retval][out] */ BSTR __RPC_FAR *pbstr) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*pbstr = SysAllocString(m_wszSPCFileName)) == NULL )
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_SPCFileName(
/* [in] */ BSTR bstr) {
return(put_SPCFileNameWStr(bstr));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_PVKFileName(
/* [retval][out] */ BSTR __RPC_FAR *pbstr) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*pbstr = SysAllocString(m_wszPVKFileName)) == NULL )
hr = E_OUTOFMEMORY;
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_PVKFileName(
/* [in] */ BSTR bstr) {
return(put_PVKFileNameWStr(bstr));
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_HashAlgorithm(
/* [retval][out] */ BSTR __RPC_FAR *pbstr) {
LPWSTR wszAlg = NULL;
HRESULT hr = S_OK;
assert(pbstr != NULL);
*pbstr = NULL;
if( (hr = get_HashAlgorithmWStr(&wszAlg)) == S_OK ) {
if( (*pbstr = SysAllocString(wszAlg)) == NULL )
hr = E_OUTOFMEMORY;
}
if(wszAlg != NULL)
MyCoTaskMemFree(wszAlg);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_HashAlgorithm(
/* [in] */ BSTR bstr) {
return(put_HashAlgorithmWStr(bstr));
}
HRESULT STDMETHODCALLTYPE CCEnroll::enumContainers(
/* [in] */ LONG dwIndex,
/* [out][retval] */ BSTR __RPC_FAR *pbstr) {
LPWSTR pwsz = NULL;
HRESULT hr;
assert(pbstr != NULL);
if((hr = enumContainersWStr(dwIndex, &pwsz)) != S_OK)
goto EnumContainerError;
if( (*pbstr = SysAllocString(pwsz)) == NULL )
{
hr = E_OUTOFMEMORY;
goto ErrorSysAllocString;
}
hr = S_OK;
ErrorReturn:
if(pwsz != NULL)
MyCoTaskMemFree(pwsz);
return(hr);
TRACE_ERROR(EnumContainerError);
TRACE_ERROR(ErrorSysAllocString);
}
HRESULT STDMETHODCALLTYPE CCEnroll::enumProviders(
/* [in] */ LONG dwIndex,
/* [in] */ LONG dwFlags,
/* [out][retval] */ BSTR __RPC_FAR *pbstrProvName) {
HRESULT hr;
LPWSTR pwszProvName = NULL;
assert(pbstrProvName != NULL);
*pbstrProvName = NULL;
if( (hr = enumProvidersWStr(dwIndex, dwFlags, &pwszProvName)) != S_OK)
goto EnumProvidersError;
if( (*pbstrProvName = SysAllocString(pwszProvName)) == NULL )
{
hr = E_OUTOFMEMORY;
goto ErrorSysAllocString;
}
hr = S_OK;
ErrorReturn:
if(pwszProvName != NULL)
MyCoTaskMemFree(pwszProvName);
return(hr);
TRACE_ERROR(EnumProvidersError);
TRACE_ERROR(ErrorSysAllocString);
}
HRESULT STDMETHODCALLTYPE CCEnroll::createFilePKCS10WStr(
/* [in] */ LPCWSTR DNName,
/* [in] */ LPCWSTR Usage,
/* [in] */ LPCWSTR wszPKCS10FileName) {
HRESULT hr;
BSTR bstrPKCS10 = NULL;
// get the pkcs 10
if( (hr = createPKCS10WStrBStr(
DNName,
Usage,
&bstrPKCS10)) != S_OK)
{
goto ErrorCreatePKCS10;
}
// save it to file
hr = BStringToFile(bstrPKCS10, wszPKCS10FileName);
if (S_OK != hr)
{
goto ErrorBStringToFile;
}
hr = S_OK;
ErrorReturn:
if(bstrPKCS10 != NULL)
SysFreeString(bstrPKCS10);
return(hr);
TRACE_ERROR(ErrorBStringToFile);
TRACE_ERROR(ErrorCreatePKCS10);
}
HRESULT STDMETHODCALLTYPE CCEnroll::acceptFilePKCS7WStr(
/* [in] */ LPCWSTR wszPKCS7FileName)
{
HRESULT hr;
CRYPT_DATA_BLOB blob;
ZeroMemory(&blob, sizeof(blob));
hr = xeStringToBinaryFromFile(
wszPKCS7FileName,
&blob.pbData,
&blob.cbData,
CRYPT_STRING_ANY);
if (S_OK != hr)
{
goto xeStringToBinaryFromFileError;
}
// accept the blob
hr = acceptPKCS7Blob(&blob);
ErrorReturn:
if (NULL != blob.pbData)
{
MyCoTaskMemFree(blob.pbData);
}
return(hr);
TRACE_ERROR(xeStringToBinaryFromFileError)
}
BOOL GetAlgAndBitLen(
HCRYPTPROV hProv,
ALG_ID * pAlg,
DWORD * pdwBitLen,
DWORD dwFlags)
{
static BOOL fNew = TRUE;
PROV_ENUMALGS_EX enumAlgsEx;
PROV_ENUMALGS enumAlgs;
DWORD cb = 0;
*pAlg = 0;
*pdwBitLen = 0;
if(fNew) {
cb = sizeof(enumAlgsEx);
if(CryptGetProvParam(
hProv,
PP_ENUMALGS_EX,
(BYTE *) &enumAlgsEx,
&cb,
dwFlags)) {
*pAlg = enumAlgsEx.aiAlgid;
*pdwBitLen = enumAlgsEx.dwMaxLen;
return(TRUE);
} else if(dwFlags != 0)
fNew = FALSE;
else
return(FALSE);
}
// otherwise do the old stuff
cb = sizeof(PROV_ENUMALGS);
if(CryptGetProvParam(
hProv,
PP_ENUMALGS,
(BYTE *) &enumAlgs,
&cb,
dwFlags) ) {
*pAlg = enumAlgs.aiAlgid;
*pdwBitLen = enumAlgs.dwBitLen;
return(TRUE);
}
return(FALSE);
}
HRESULT
CreateSMimeExtension(
IN HCRYPTPROV hProv,
OUT BYTE **ppbSMime,
OUT DWORD *pcbSMime)
{
#define CINCSMIMECAP 20
HRESULT hr;
DWORD dwBitLen;
DWORD i;
DWORD cbE;
BYTE *pbE = NULL;
DWORD dwFlags;
PCCRYPT_OID_INFO pOidInfo = NULL;
CRYPT_SMIME_CAPABILITIES smimeCaps;
DWORD crgsmimeCap = 0;
ALG_ID AlgID;
BYTE *pb = NULL;
DWORD cb = 0;
memset(&smimeCaps, 0, sizeof(CRYPT_SMIME_CAPABILITIES));
smimeCaps.rgCapability = (PCRYPT_SMIME_CAPABILITY) LocalAlloc(LPTR, CINCSMIMECAP * sizeof(CRYPT_SMIME_CAPABILITY));
if (NULL == smimeCaps.rgCapability)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
crgsmimeCap = CINCSMIMECAP;
dwFlags = CRYPT_FIRST; //first item
while (GetAlgAndBitLen(hProv, &AlgID, &dwBitLen, dwFlags))
{
pbE = NULL;
cbE = 0;
dwFlags = 0; //next item
if(ALG_CLASS_DATA_ENCRYPT == GET_ALG_CLASS(AlgID))
{
if(AlgID == CALG_RC2 || AlgID == CALG_RC4)
{
// encode the usage
while (TRUE)
{
if(!CryptEncodeObject(
CRYPT_ASN_ENCODING,
X509_INTEGER,
&dwBitLen,
pbE, // pbEncoded
&cbE))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptEncodeObjectError;
}
if (NULL != pbE)
{
break;
}
pbE = (BYTE *)LocalAlloc(LPTR, cbE);
if (NULL == pbE)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
}
} else {
continue;
}
// convert to an oid,
pOidInfo = xeCryptFindOIDInfo(
CRYPT_OID_INFO_ALGID_KEY,
(void *) &AlgID,
CRYPT_ENCRYPT_ALG_OID_GROUP_ID);
if(NULL == pOidInfo)
{
// don't crash on an error, just say we don't known it.
if (NULL != pbE) {
LocalFree(pbE);
}
pbE = NULL;
continue;
}
// make sure we have enough room
if(smimeCaps.cCapability >= crgsmimeCap)
{
PCRYPT_SMIME_CAPABILITY pSmimeCapsTmp;
//increment the size
crgsmimeCap += CINCSMIMECAP;
pSmimeCapsTmp = (PCRYPT_SMIME_CAPABILITY)LocalReAlloc(
smimeCaps.rgCapability,
crgsmimeCap * sizeof(CRYPT_SMIME_CAPABILITY),
LMEM_MOVEABLE | LMEM_ZEROINIT);
if(NULL == pSmimeCapsTmp)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
smimeCaps.rgCapability = pSmimeCapsTmp;
}
smimeCaps.rgCapability[smimeCaps.cCapability].pszObjId = (char *) pOidInfo->pszOID;
smimeCaps.rgCapability[smimeCaps.cCapability].Parameters.pbData = pbE;
smimeCaps.rgCapability[smimeCaps.cCapability].Parameters.cbData = cbE;
smimeCaps.cCapability++;
pbE = NULL; // We'll free pbE through the struct we just assigned. NULL out so we don't double-free.
}
// encode the capabilities
while (TRUE)
{
if (!CryptEncodeObject(
CRYPT_ASN_ENCODING,
PKCS_SMIME_CAPABILITIES,
&smimeCaps,
pb,
&cb))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptEncodeObjectError;
}
if (NULL != pb)
{
break;
}
pb = (BYTE *)LocalAlloc(LPTR, cb);
}
*ppbSMime = pb;
*pcbSMime = cb;
pb = NULL;
hr = S_OK;
ErrorReturn:
if(NULL != smimeCaps.rgCapability)
{
for (i = 0; i < smimeCaps.cCapability; ++i)
{
if (NULL != smimeCaps.rgCapability[i].Parameters.pbData)
{
LocalFree(smimeCaps.rgCapability[i].Parameters.pbData);
}
}
LocalFree(smimeCaps.rgCapability);
}
if(NULL != pb)
{
LocalFree(pb);
}
if (NULL != pbE)
{
LocalFree(pbE);
}
return hr;
TRACE_ERROR(CryptEncodeObjectError)
TRACE_ERROR(OutOfMemoryError)
}
#if DBG
void DebugGetContainerSD(HCRYPTPROV hProv)
{
PSECURITY_DESCRIPTOR pSD = NULL;
DWORD cbSD;
while (TRUE)
{
if (!CryptGetProvParam(
hProv,
PP_KEYSET_SEC_DESCR,
(BYTE*)pSD,
&cbSD,
DACL_SECURITY_INFORMATION))
{
break;
}
if (NULL != pSD)
{
break;
}
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, cbSD);
if (NULL == pSD)
{
break;
}
}
if (NULL != pSD)
{
LocalFree(pSD);
}
}
#endif //DBG
//get the current user sids
HRESULT
GetCurrentUserInfo(
OUT PTOKEN_USER *ppUserInfo,
OUT BOOL *pfAdmin)
{
HRESULT hr;
PTOKEN_USER pUserInfo = NULL;
DWORD dwSize = 0;
HANDLE hToken = NULL;
HANDLE hDupToken = NULL;
PSID psidAdministrators = NULL;
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
HANDLE hThread;
HANDLE hProcess;
//init
*pfAdmin = FALSE;
if (!AllocateAndInitializeSid(
&siaNtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,
0,
0,
0,
0,
0,
&psidAdministrators))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto AllocateAndInitializeSidError;
}
hThread = GetCurrentThread();
if (NULL == hThread)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto GetCurrentThreadError;
}
// Get the access token for current thread
if (!OpenThreadToken(
hThread,
TOKEN_QUERY | TOKEN_DUPLICATE,
FALSE,
&hToken))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
if(HRESULT_FROM_WIN32(ERROR_NO_TOKEN) != hr)
{
goto OpenThreadTokenError;
}
//get process token instead
hProcess = GetCurrentProcess();
if (NULL == hProcess)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto GetCurrentProcessError;
}
hToken = NULL;
if (!OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE, &hToken))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto OpenProcessTokenError;
}
}
// CheckTokenMembership must operate on impersonation token, so make one
if (!DuplicateToken(hToken, SecurityIdentification, &hDupToken))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto DuplicateTokenError;
}
if (!MyCheckTokenMembership(hDupToken, psidAdministrators, pfAdmin))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CheckTokenMembershipError;
}
//get current user sid
while (TRUE)
{
if (!GetTokenInformation(
hToken,
TokenUser,
pUserInfo,
dwSize,
&dwSize))
{
if (NULL != pUserInfo ||
ERROR_INSUFFICIENT_BUFFER != GetLastError())
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto GetTokenInformationError;
}
}
if (NULL != pUserInfo)
{
//done
break;
}
pUserInfo = (PTOKEN_USER)LocalAlloc(LMEM_FIXED, dwSize);
if (NULL == pUserInfo)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
}
if (NULL != ppUserInfo)
{
*ppUserInfo = pUserInfo;
pUserInfo = NULL;
}
hr = S_OK;
ErrorReturn:
if (NULL != pUserInfo)
{
LocalFree(pUserInfo);
}
if (NULL != hToken)
{
CloseHandle(hToken);
}
if (NULL != hDupToken)
{
CloseHandle(hDupToken);
}
if (NULL != psidAdministrators)
{
FreeSid(psidAdministrators);
}
return hr;
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(GetTokenInformationError)
TRACE_ERROR(OpenProcessTokenError)
TRACE_ERROR(GetCurrentProcessError)
TRACE_ERROR(CheckTokenMembershipError)
TRACE_ERROR(DuplicateTokenError)
TRACE_ERROR(OpenThreadTokenError)
TRACE_ERROR(GetCurrentThreadError)
TRACE_ERROR(AllocateAndInitializeSidError)
}
HRESULT
SetKeyContainerSecurityForNULLDacl(
HCRYPTPROV hProv,
DWORD dwFlags,
PTOKEN_USER pUserInfo)
{
DWORD ccNeeded;
HRESULT hr;
LPWSTR wszSD = NULL;
LPWSTR wszUserSid = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
UNREFERENCED_PARAMETER(dwFlags);
// We want the security descriptor for the new keyset to look like so:
//
// ACES:
// NT AUTHORITY\SYSTEM:F
// BUILTIN\Administrators:F
//
//
#define SDDL_NEW_KEYSET_START L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;"
#define SDDL_NEW_KEYSET_END L")"
if (!ConvertSidToStringSidW(pUserInfo->User.Sid, &wszUserSid)) {
goto ConvertSidToStringSidError;
}
ccNeeded = (DWORD)(wcslen(SDDL_NEW_KEYSET_START) + wcslen(wszUserSid) + wcslen(SDDL_NEW_KEYSET_END) + 1);
wszSD = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*ccNeeded);
if (NULL == wszSD) {
goto MemoryError;
}
wcscpy(wszSD, SDDL_NEW_KEYSET_START);
wcscat(wszSD, wszUserSid);
wcscat(wszSD, SDDL_NEW_KEYSET_END);
if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(wszSD, SDDL_REVISION_1, &pSD, NULL)) {
goto ConvertStringSecurityDescriptorToSecurityDescriptorWError;
}
if (!IsValidSecurityDescriptor(pSD)) {
goto IsValidSecurityDescriptorError;
}
#if DBG
DebugGetContainerSD(hProv); //just for ntsd debug
#endif
if (!CryptSetProvParam(hProv, PP_KEYSET_SEC_DESCR, (BYTE*)pSD, DACL_SECURITY_INFORMATION)) {
goto CryptSetProvParamError;
}
#if DBG
DebugGetContainerSD(hProv); //just for ntsd debug
#endif
hr = S_OK;
ErrorReturn:
if (NULL != wszSD)
{
LocalFree(wszSD);
}
if (NULL != wszUserSid)
{
LocalFree(wszUserSid);
}
if (NULL != pSD)
{
LocalFree(pSD);
}
return hr;
SET_HRESULT(ConvertSidToStringSidError, HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(ConvertStringSecurityDescriptorToSecurityDescriptorWError, HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(CryptSetProvParamError, HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(IsValidSecurityDescriptorError, HRESULT_FROM_WIN32(ERROR_INVALID_SECURITY_DESCR));
SET_HRESULT(MemoryError, E_OUTOFMEMORY);
}
// This function ACLs new keysets (as 3rd party CSPs and downlevel CSPs may not do this correctly.
// Should *not* call this function on existing keysets (as ACLs may have been set differently by admins)
//
HRESULT
SetKeyContainerSecurity(
HCRYPTPROV hProv,
DWORD dwFlags)
{
HRESULT hr;
PSECURITY_DESCRIPTOR pNewSD = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
DWORD cbSD;
ACL_SIZE_INFORMATION AclInfo;
PTOKEN_USER pUserInfo = NULL;
PACL pNewAcl = NULL;
LPVOID pAce;
DWORD dwIndex;
BYTE AceType;
PACL pAcl;
BOOL fDacl = TRUE;
BOOL fDef = FALSE;
BOOL fAdmin;
BOOL fKeepSystemSid;
BOOL fMachineKeySet = (0x0 != (dwFlags & CRYPT_MACHINE_KEYSET)) ?
TRUE : FALSE;
PSID pSidSystem = NULL;
PSID pSidAdministrators = NULL;
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
//get the current user info
hr = GetCurrentUserInfo(&pUserInfo, &fAdmin);
if (S_OK != hr)
{
goto GetCurrentUserInfoError;
}
//get the current sd from key container
while (TRUE)
{
if (!CryptGetProvParam(
hProv,
PP_KEYSET_SEC_DESCR,
(BYTE*)pSD,
&cbSD,
DACL_SECURITY_INFORMATION))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptGetProvParamError;
}
if (NULL != pSD)
{
break;
}
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, cbSD);
if (NULL == pSD)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
//get acl from sd
if (!GetSecurityDescriptorDacl(
pSD,
&fDacl,
&pAcl,
&fDef))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto GetSecurityDescriptorDaclError;
}
if (!fDacl)
{
//if no dacl, quit
hr = MY_HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
goto GetSecurityDescriptorDaclError;
}
if (NULL == pAcl)
{
#if 0
//this means allow everyone access the key which is unexpected,
hr = SetKeyContainerSecurityForNULLDacl(hProv, dwFlags, pUserInfo);
#endif
// BUGBUG: The NULL DACL stuff doesn't work on downlevels because it requires SDDL. We don't
// have time to fix for windows update, but we should revisit this later
hr = S_OK;
goto done;
}
//get acl info
if (!GetAclInformation(
pAcl,
&AclInfo,
sizeof(AclInfo),
AclSizeInformation))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto GetAclInformationError;
}
//allocate enough for new dacl since we just remove aces
pNewAcl = (PACL)LocalAlloc(LMEM_ZEROINIT, AclInfo.AclBytesInUse);
if (NULL == pNewAcl)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
if (!InitializeAcl(pNewAcl, AclInfo.AclBytesInUse, ACL_REVISION))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto InitializeAclError;
}
fKeepSystemSid = fAdmin && fMachineKeySet;
if (fKeepSystemSid)
{
//get system sid to later use
if (!AllocateAndInitializeSid(
&siaNtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0,
0,
0,
0,
0,
0,
0,
&pSidSystem))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto AllocateAndInitializeSidError;
}
}
if (!AllocateAndInitializeSid(&siaNtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pSidAdministrators
))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto AllocateAndInitializeSidError;
}
//go through each ace, get only current user aces
for (dwIndex = 0; dwIndex < AclInfo.AceCount; ++dwIndex)
{
if (!GetAce(pAcl, dwIndex, &pAce))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto GetAceError;
}
AceType = ((ACCESS_ALLOWED_ACE*)pAce)->Header.AceType;
if (ACCESS_ALLOWED_ACE_TYPE == AceType)
{
if (EqualSid(pUserInfo->User.Sid, (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart))
|| (fKeepSystemSid && EqualSid(pSidSystem, (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)))
|| EqualSid(pSidAdministrators, (PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)))
{
//add current user ace or system ace into new acl
if (!AddAccessAllowedAce(
pNewAcl,
ACL_REVISION,
((PACCESS_ALLOWED_ACE)pAce)->Mask,
(PSID)&(((PACCESS_ALLOWED_ACE)pAce)->SidStart)))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto AddAccessAllowedAceError;
}
}
}
else if (ACCESS_DENIED_ACE_TYPE == AceType)
{
//add all deny ace into new acl
if (!AddAccessDeniedAce(
pNewAcl,
ACL_REVISION,
((PACCESS_ALLOWED_ACE)pAce)->Mask,
(PSID)&(((PACCESS_DENIED_ACE)pAce)->SidStart)))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto AddAccessDeniedAceError;
}
}
}
// initialize a security descriptor.
pNewSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (pNewSD == NULL)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
if (!InitializeSecurityDescriptor(pNewSD, SECURITY_DESCRIPTOR_REVISION))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto InitializeSecurityDescriptorError;
}
// add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(
pNewSD,
TRUE, // fDaclPresent flag
pNewAcl,
FALSE)) // not a default DACL
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto SetSecurityDescriptorDaclError;
}
//ok, set sd to be protected
if (!MySetSecurityDescriptorControl(
pNewSD,
SE_DACL_PROTECTED,
SE_DACL_PROTECTED))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto SetSecurityDescriptorControlError;
}
if (!IsValidSecurityDescriptor(pNewSD))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto IsValidSecurityDescriptorError;
}
#if DBG
DebugGetContainerSD(hProv); //just for ntsd debug
#endif
//now we just set it
if (!CryptSetProvParam(
hProv,
PP_KEYSET_SEC_DESCR,
(BYTE*)pNewSD,
DACL_SECURITY_INFORMATION))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptSetProvParamError;
}
#if DBG
DebugGetContainerSD(hProv); //just for ntsd debug
#endif
done:
hr = S_OK;
ErrorReturn:
if (NULL != pSD)
{
LocalFree(pSD);
}
if (NULL != pUserInfo)
{
LocalFree(pUserInfo);
}
if (NULL != pNewAcl)
{
LocalFree(pNewAcl);
}
if (NULL != pNewSD)
{
LocalFree(pNewSD);
}
if (NULL != pSidSystem)
{
FreeSid(pSidSystem);
}
if (NULL != pSidAdministrators)
{
FreeSid(pSidAdministrators);
}
return hr;
TRACE_ERROR(CryptSetProvParamError)
TRACE_ERROR(SetSecurityDescriptorDaclError)
TRACE_ERROR(InitializeSecurityDescriptorError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(AddAccessAllowedAceError)
TRACE_ERROR(AddAccessDeniedAceError)
TRACE_ERROR(GetAceError)
TRACE_ERROR(GetCurrentUserInfoError)
TRACE_ERROR(InitializeAclError)
TRACE_ERROR(GetAclInformationError)
TRACE_ERROR(GetSecurityDescriptorDaclError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptGetProvParamError)
TRACE_ERROR(SetSecurityDescriptorControlError)
TRACE_ERROR(IsValidSecurityDescriptorError)
TRACE_ERROR(AllocateAndInitializeSidError)
}
HRESULT STDMETHODCALLTYPE CCEnroll::createPKCS10WStr(
/* [in] */ LPCWSTR DNName,
/* [in] */ LPCWSTR wszPurpose,
/* [out] */ PCRYPT_DATA_BLOB pPkcs10Blob)
{
#define EndExt 5
#define EndAttr 6
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
CERT_REQUEST_INFO reqInfo;
CERT_EXTENSIONS Extensions;
PCERT_EXTENSION pExtCur = NULL;
PCERT_EXTENSION rgExtension = NULL;
CRYPT_ATTRIBUTE rgAttribute[EndAttr];
CRYPT_ATTR_BLOB blobExt;
CRYPT_ATTR_BLOB blobCSPAttr;
CRYPT_CSP_PROVIDER CSPProvider;
CRYPT_ATTR_BLOB blobOSVAttr;
CRYPT_ATTR_BLOB blobSMIMEPKCS7;
CERT_NAME_VALUE cnvOSVer;
OSVERSIONINFO osvInfo;
DWORD iExt = 0;
CRYPT_BIT_BLOB bbKeyUsage;
BYTE bKeyUsage;
CERT_SIGNED_CONTENT_INFO SignatureInfo;
HRESULT hr = S_OK;
DWORD errBefore = GetLastError();
PCCERT_CONTEXT pCertContext = NULL;
HCERTSTORE hStore = NULL;
DWORD ssFlags = 0;
HANDLE hFile = NULL;
CRYPT_DATA_BLOB blobData;
DWORD cb = 0;
char * pszPurpose = NULL;
char * szStart = NULL;
char * szEnd = NULL;
char szVersion[45] = {0};
BOOL fAddCodeSign = FALSE;
DWORD cPassedEKU = 0;
DWORD i = 0;
BOOL fRet;
BYTE *pbSMime = NULL;
BYTE *pbKU = NULL;
BYTE *pbEKU = NULL;
PPROP_STACK pProp;
CRYPT_ATTR_BLOB blobClientId;
DWORD cPublicKeyInfo = 0;
BYTE *pbSubjectKeyHashExtension = NULL;
DWORD cbSubjectKeyHashExtension = 0;
DWORD dwErr;
LPWSTR pwszNotSafeRequesting = NULL;
LPWSTR pwszTitle = NULL;
//
// Declaration of extensions we need. The extensions with matching OIDs will be added
// to the temporary cert context created by this method.
//
LPSTR rgszExtensionOIDs[] = {
szOID_ENROLL_CERTTYPE_EXTENSION,
szOID_CERTIFICATE_TEMPLATE
};
// An array of the extensions we need to add to the certificate
CERT_EXTENSION rgNeededExtensions[sizeof(rgszExtensionOIDs) / sizeof(LPSTR)];
// Need to put the array in a CERT_EXTENSIONS struct.
CERT_EXTENSIONS ceExtensions;
ceExtensions.rgExtension = &rgNeededExtensions[0];
ceExtensions.cExtension = 0;
CRYPT_KEY_PROV_INFO keyProvInfoT;
CERT_ENHKEY_USAGE enhKeyUsage;
CRYPT_DATA_BLOB blobPKCS7;
CRYPT_DATA_BLOB blobRenewAttr;
RequestFlags requestFlags;
CRYPT_DATA_BLOB requestInfoBlob;
CRYPT_DATA_BLOB blobRenewalCert;
ALG_ID rgAlg[2];
PCCRYPT_OID_INFO pOidInfo = NULL;
EnterCriticalSection(&m_csXEnroll);
// for the life of our procedure.
SetLastError(ERROR_SUCCESS);
assert(pPkcs10Blob != NULL);
// clean out the PKCS 10
memset(&Extensions, 0, sizeof(CERT_EXTENSIONS));
memset(&rgAttribute, 0, sizeof(rgAttribute));
memset(&reqInfo, 0, sizeof(CERT_REQUEST_INFO));
memset(&SignatureInfo, 0, sizeof(SignatureInfo));
memset(&blobData, 0, sizeof(CRYPT_DATA_BLOB));
memset(&enhKeyUsage, 0, sizeof(CERT_ENHKEY_USAGE ));
memset(pPkcs10Blob, 0, sizeof(CRYPT_DATA_BLOB));
memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
memset(&blobRenewAttr, 0, sizeof(CRYPT_DATA_BLOB));
memset(&requestFlags, 0, sizeof(RequestFlags));
memset(&requestInfoBlob, 0, sizeof(CRYPT_DATA_BLOB));
memset(&CSPProvider, 0, sizeof(CRYPT_CSP_PROVIDER));
memset(&cnvOSVer, 0, sizeof(CERT_NAME_VALUE));
memset(&osvInfo, 0, sizeof(OSVERSIONINFO));
memset(&blobSMIMEPKCS7, 0, sizeof(CRYPT_ATTR_BLOB));
memset(&rgNeededExtensions[0], 0, sizeof(rgNeededExtensions));
ZeroMemory(&blobExt, sizeof(blobExt));
memset(&blobCSPAttr, 0, sizeof(CRYPT_ATTR_BLOB));
memset(&blobOSVAttr, 0, sizeof(CRYPT_ATTR_BLOB));
memset(&blobClientId, 0, sizeof(CRYPT_ATTR_BLOB));
reqInfo.dwVersion = CERT_REQUEST_V1;
// Creating a request is not safe for scripting: pop up a warning dialog if called from script
if (0 != m_dwEnabledSafteyOptions) {
hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
if (S_OK != hr) {
SetLastError(hr);
goto xeLoadRCStringError;
}
hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_REQUESTING_CERT, &pwszNotSafeRequesting);
if (S_OK != hr) {
SetLastError(hr);
goto xeLoadRCStringError;
}
if (IDYES != MessageBoxU(NULL, pwszNotSafeRequesting, pwszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2)) {
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
SetLastError(hr);
goto CancelledError;
}
}
if(!m_fUseExistingKey)
{
// attempt to get a new keyset
if((hProv = GetProv(CRYPT_NEWKEYSET)) == NULL) {
// in the hardware token case, there may only be a finite number of containers
// if you run out, then use the default container. The Default container can
// be specified by either a NULL or empty container name.
// this is behavior requested by the smart cards, in particular smart card enrollment.
if( m_fReuseHardwareKeyIfUnableToGenNew &&
GetLastError() == NTE_TOKEN_KEYSET_STORAGE_FULL) {
// set it to the default container name
if( m_keyProvInfo.pwszContainerName != wszEmpty )
MyCoTaskMemFree(m_keyProvInfo.pwszContainerName);
m_keyProvInfo.pwszContainerName = wszEmpty;
// say we want to use an exiting key.
m_fUseExistingKey = TRUE;
}
else
goto ErrorCryptAcquireContext;
}
}
// if we are to use an existing key
if(m_fUseExistingKey) {
if((hProv = GetProv(0)) == NULL)
goto ErrorCryptAcquireContext;
}
// we have the keyset, now make sure we have the key gen'ed
if(!CryptGetUserKey(
hProv,
m_keyProvInfo.dwKeySpec,
&hKey))
{
//in case of smartcard csp, above call could be failed from
//PIN Cancel button, don't go next to try genkey
//also notice different csp could return different cancel errors
dwErr = GetLastError();
if (SCARD_W_CANCELLED_BY_USER == dwErr ||
ERROR_CANCELLED == dwErr ||
ERROR_ACCESS_DENIED == dwErr)
{
goto CryptGetUserKeyCancelError;
}
// doesn't exist so gen it
assert(hKey == NULL);
// if the cached key is non-NULL, free it to prevent memory leaks
if (NULL != m_hCachedKey)
{
CryptDestroyKey(m_hCachedKey);
m_hCachedKey = NULL;
}
if(!CryptGenKey( hProv,
m_keyProvInfo.dwKeySpec,
m_dwGenKeyFlags | CRYPT_ARCHIVABLE,
&m_hCachedKey) )
{
//could be cancelled by user? don't make next try
dwErr = GetLastError();
if (SCARD_W_CANCELLED_BY_USER == dwErr ||
ERROR_CANCELLED == dwErr ||
ERROR_ACCESS_DENIED == dwErr)
{
goto ErrorCryptGenKey;
}
//this error may be caused by not supporting CRYPT_ARCHIVABLE
//we should check against error NTE_BAD_FLAGS but I doubt all
//csps return consistent error code
//let's try one more time without archivable flag
assert(NULL == m_hCachedKey);
DWORD dwGenKeyFlags = m_dwGenKeyFlags;
if (NULL != m_PrivateKeyArchiveCertificate && m_fNewRequestMethod && (0 == (dwGenKeyFlags & CRYPT_EXPORTABLE)))
{
// We want ARCHIVABLE but not EXPORTABLE, so we needed the CRYPT_ARCHIVAL bit. Give up.
goto ErrorCryptGenKey;
}
if (!CryptGenKey(
hProv,
m_keyProvInfo.dwKeySpec,
dwGenKeyFlags,
&hKey))
{
goto ErrorCryptGenKey;
}
}
//try to set key container ACL with owner ACE only (NOTE: only do this for creation!)
hr = SetKeyContainerSecurity(hProv, m_keyProvInfo.dwFlags);
#if DBG
if (S_OK != hr)
{
goto SetKeyContainerSecurityError;
}
#endif //DBG
hr = S_OK; //free build, no error checking here, if fails, live with it
}
if (NULL != hKey)
{
// don't need the hKey on existing key, so get rid of it
CryptDestroyKey(hKey);
}
if ((NULL == m_PrivateKeyArchiveCertificate || !m_fNewRequestMethod) &&
NULL != m_hCachedKey)
{
//we don't need cache it, destroy it as soon as key is gen(ed)
CryptDestroyKey(m_hCachedKey);
m_hCachedKey = NULL;
}
// now get the public key out into m_pPublicKeyInfo
// m_pPublicKeyInfo is internal use for cache
if (NULL != m_pPublicKeyInfo)
{
LocalFree(m_pPublicKeyInfo);
m_pPublicKeyInfo = NULL;
}
while (TRUE)
{
if(!CryptExportPublicKeyInfo(hProv,
m_keyProvInfo.dwKeySpec,
X509_ASN_ENCODING,
m_pPublicKeyInfo,
&cPublicKeyInfo))
{
goto ErrorCryptExportPublicKeyInfo;
}
if (NULL != m_pPublicKeyInfo)
{
break;
}
m_pPublicKeyInfo = (PCERT_PUBLIC_KEY_INFO)LocalAlloc(
LMEM_FIXED, cPublicKeyInfo);
if (NULL == m_pPublicKeyInfo)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
reqInfo.SubjectPublicKeyInfo = *m_pPublicKeyInfo;
// get the Subject DN only if one is specified
reqInfo.Subject.pbData = NULL;
while (TRUE)
{
if( !MyCertStrToNameW(
CRYPT_ASN_ENCODING,
DNName,
0 | m_dwT61DNEncoding,
NULL,
reqInfo.Subject.pbData,
&reqInfo.Subject.cbData,
NULL))
{
if (CRYPT_E_INVALID_X500_STRING == GetLastError() &&
L'\0' == DNName[0])
{
//this is likely on W95, W98, or NT4 with some IEs
//crypt32 doesn't support empty DN conversion
//hard code here
reqInfo.Subject.cbData = 2;
reqInfo.Subject.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
reqInfo.Subject.cbData);
if (NULL == reqInfo.Subject.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
reqInfo.Subject.pbData[0] = 0x30;
reqInfo.Subject.pbData[1] = 0x0;
//done
break;
}
else
{
goto ErrorCertStrToNameW;
}
}
if (NULL != reqInfo.Subject.pbData)
{
break;
}
reqInfo.Subject.pbData = (BYTE *)LocalAlloc(LMEM_FIXED,
reqInfo.Subject.cbData);
if (NULL == reqInfo.Subject.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
// allocate room for the extensions
cb = (CountStackExtension(m_fNewRequestMethod) + EndExt) * sizeof(CERT_EXTENSION);
rgExtension = (PCERT_EXTENSION)LocalAlloc(LMEM_FIXED, cb);
if (NULL == rgExtension)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
memset(rgExtension, 0, cb);
cb = 0;
if (!m_fUseClientKeyUsage)
{
// Make Key Usage
rgExtension[iExt].pszObjId = szOID_KEY_USAGE;
rgExtension[iExt].fCritical = TRUE;
// AT_SIGNATURE
if( m_keyProvInfo.dwKeySpec == AT_SIGNATURE)
bKeyUsage =
CERT_DIGITAL_SIGNATURE_KEY_USAGE |
CERT_NON_REPUDIATION_KEY_USAGE;
//AT_KEYEXCHANGE, limited for EMAIL single use
// email may not work if signature is present
else if(m_fLimitExchangeKeyToEncipherment)
bKeyUsage =
CERT_KEY_ENCIPHERMENT_KEY_USAGE |
CERT_DATA_ENCIPHERMENT_KEY_USAGE;
// AT_KEYEXCHANGE and AT_SIGNATURE dual key
// This is the normal case for AT_KEYEXCHANGE since CAPI will sign with this.
else
bKeyUsage =
CERT_KEY_ENCIPHERMENT_KEY_USAGE |
CERT_DATA_ENCIPHERMENT_KEY_USAGE |
CERT_DIGITAL_SIGNATURE_KEY_USAGE |
CERT_NON_REPUDIATION_KEY_USAGE;
bbKeyUsage.pbData = &bKeyUsage;
bbKeyUsage.cbData = 1;
bbKeyUsage.cUnusedBits = 1;
// encode the usage
rgExtension[iExt].Value.pbData = NULL;
while (TRUE)
{
if(!CryptEncodeObject(
CRYPT_ASN_ENCODING,
X509_KEY_USAGE,
&bbKeyUsage,
pbKU,
&rgExtension[iExt].Value.cbData))
{
goto ErrorEncodeKeyUsage;
}
if (NULL != pbKU)
{
rgExtension[iExt].Value.pbData = pbKU;
//done
break;
}
pbKU = (BYTE *)LocalAlloc(LMEM_FIXED, rgExtension[iExt].Value.cbData);
if (NULL == pbKU)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
iExt++;
}
if(m_fEnableSMIMECapabilities)
{
// add SMIME extension for symmetric algorithms
rgExtension[iExt].pszObjId = szOID_RSA_SMIMECapabilities;
rgExtension[iExt].fCritical = FALSE;
hr = CreateSMimeExtension(
hProv,
&pbSMime,
&rgExtension[iExt].Value.cbData);
if (S_OK != hr)
{
goto CreateSMimeExtensionError;
}
rgExtension[iExt].Value.pbData = pbSMime;
iExt++;
}
if (m_fHonorIncludeSubjectKeyID && m_fIncludeSubjectKeyID)
{
hr = myCreateSubjectKeyIdentifierExtension(
m_pPublicKeyInfo,
&pbSubjectKeyHashExtension,
&cbSubjectKeyHashExtension);
if (S_OK != hr)
{
goto myCreateSubjectKeyIdentifierExtensionError;
}
//add subject key ID hash extension into PKCS10
rgExtension[iExt].pszObjId = szOID_SUBJECT_KEY_IDENTIFIER;
rgExtension[iExt].fCritical = FALSE;
rgExtension[iExt].Value.pbData = pbSubjectKeyHashExtension;
rgExtension[iExt].Value.cbData = cbSubjectKeyHashExtension;
iExt++;
}
if(wszPurpose != NULL) {
cb = 0;
while (TRUE)
{
if(0 == (cb = WideCharToMultiByte(
0, 0, wszPurpose, -1, pszPurpose, cb, NULL, NULL)))
{
SetLastError(ERROR_OUTOFMEMORY);
goto ErrorCantConvertPurpose;
}
if (NULL != pszPurpose)
{
break;
}
pszPurpose = (CHAR*)LocalAlloc(LMEM_FIXED, cb);
if (NULL == pszPurpose)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
szStart = pszPurpose;
// remove leading blanks
while(*szStart == ',' || *szStart == ' ')
*szStart++ = '\0';
while( szStart[0] != '\0' ) {
// find the next string
szEnd = szStart;
while(*szEnd != ',' && *szEnd != ' ' && *szEnd != '\0')
szEnd++;
// remove trailing blanks
while(*szEnd == ',' || *szEnd == ' ')
*szEnd++ = '\0';
enhKeyUsage.cUsageIdentifier++;
// see if this implies codesigning
fAddCodeSign |= !strcmp(szStart, SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID) ||
!strcmp(szStart, SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID);
// go to next string
szStart = szEnd;
}
// count the codesign EKU once
cPassedEKU = enhKeyUsage.cUsageIdentifier;
if(fAddCodeSign)
enhKeyUsage.cUsageIdentifier++;
// encode the extension
if(enhKeyUsage.cUsageIdentifier != 0) {
// allocate the EKU array
enhKeyUsage.rgpszUsageIdentifier = (LPSTR *)LocalAlloc(LMEM_FIXED,
enhKeyUsage.cUsageIdentifier * sizeof(LPSTR));
if (NULL == enhKeyUsage.rgpszUsageIdentifier)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
// add the EKU's
szStart = pszPurpose;
for(i=0; i<cPassedEKU; i++) {
while(*szStart == '\0')
szStart++;
enhKeyUsage.rgpszUsageIdentifier[i] = szStart;
while(*szStart != '\0')
szStart++;
}
// add the code sign EKU
if(fAddCodeSign)
enhKeyUsage.rgpszUsageIdentifier[enhKeyUsage.cUsageIdentifier - 1] = szOID_PKIX_KP_CODE_SIGNING;
// Deal with the policy, or purpose
rgExtension[iExt].pszObjId = szOID_ENHANCED_KEY_USAGE ;
rgExtension[iExt].fCritical = FALSE;
// encode the enhanced key usage
rgExtension[iExt].Value.cbData = 0;
while (TRUE)
{
if(!CryptEncodeObject(
CRYPT_ASN_ENCODING, X509_ENHANCED_KEY_USAGE,
&enhKeyUsage,
pbEKU, // pbEncoded
&rgExtension[iExt].Value.cbData))
{
goto ErrorEncodeEnhKeyUsage;
}
if (NULL != pbEKU)
{
//got it, done
rgExtension[iExt].Value.pbData = pbEKU;
break;
}
pbEKU = (BYTE *)LocalAlloc(LMEM_FIXED,
rgExtension[iExt].Value.cbData);
if (NULL == pbEKU)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
iExt++;
}
}
assert(EndExt >= iExt);
// now add all of the user defined extensions
pExtCur = NULL;
while(NULL != (pExtCur = EnumStackExtension(pExtCur, m_fNewRequestMethod)) ) {
rgExtension[iExt] = *pExtCur;
iExt++;
}
// fill in the extensions structure
Extensions.cExtension = iExt;
Extensions.rgExtension = rgExtension;
// encode the extensions
reqInfo.cAttribute = 0;
reqInfo.rgAttribute = rgAttribute;
while (TRUE)
{
if(!CryptEncodeObject(
CRYPT_ASN_ENCODING, X509_EXTENSIONS,
&Extensions,
blobExt.pbData, // pbEncoded
&blobExt.cbData))
{
goto ErrorEncodeExtensions;
}
if (NULL != blobExt.pbData)
{
//got it, done
break;
}
blobExt.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, blobExt.cbData);
if (NULL == blobExt.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
if (m_fOID_V2)
{
//use new rsa oid
rgAttribute[reqInfo.cAttribute].pszObjId = szOID_RSA_certExtensions;
}
else
{
//use microsoft oid for w2k clients
rgAttribute[reqInfo.cAttribute].pszObjId = szOID_CERT_EXTENSIONS;
}
rgAttribute[reqInfo.cAttribute].cValue = 1;
rgAttribute[reqInfo.cAttribute].rgValue = &blobExt;
// put in the CSP attribute
if( !GetSignatureFromHPROV(
hProv,
&CSPProvider.Signature.pbData,
&CSPProvider.Signature.cbData
) )
goto ErrorGetSignatureFromHPROV;
CSPProvider.pwszProviderName = m_keyProvInfo.pwszProvName;
CSPProvider.dwKeySpec = m_keyProvInfo.dwKeySpec;
while (TRUE)
{
if( !CryptEncodeObject(
CRYPT_ASN_ENCODING,
szOID_ENROLLMENT_CSP_PROVIDER,
&CSPProvider,
blobCSPAttr.pbData, // pbEncoded
&blobCSPAttr.cbData))
{
goto ErrorEncodeCSPAttr;
}
if (NULL != blobCSPAttr.pbData)
{
//got it, done
break;
}
blobCSPAttr.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, blobCSPAttr.cbData);
if (NULL == blobCSPAttr.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
reqInfo.cAttribute++;
rgAttribute[reqInfo.cAttribute].pszObjId = szOID_ENROLLMENT_CSP_PROVIDER;
rgAttribute[reqInfo.cAttribute].cValue = 1;
rgAttribute[reqInfo.cAttribute].rgValue = &blobCSPAttr;
// get the OSVersion
osvInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(!GetVersionExA(&osvInfo))
goto ErrorGetVersionEx;
wsprintfA(szVersion, "%d.%d.%d.%d",
osvInfo.dwMajorVersion,
osvInfo.dwMinorVersion,
osvInfo.dwBuildNumber,
osvInfo.dwPlatformId);
cnvOSVer.dwValueType = CERT_RDN_IA5_STRING;
cnvOSVer.Value.cbData = (DWORD)strlen(szVersion);
cnvOSVer.Value.pbData = (BYTE *) szVersion;
while (TRUE)
{
if(!CryptEncodeObject(
CRYPT_ASN_ENCODING,
X509_ANY_STRING,
&cnvOSVer,
blobOSVAttr.pbData, // pbEncoded
&blobOSVAttr.cbData))
{
goto ErrorEncodeOSVAttr;
}
if (NULL != blobOSVAttr.pbData)
{
//got it, done
break;
}
blobOSVAttr.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, blobOSVAttr.cbData);
if (NULL == blobOSVAttr.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
reqInfo.cAttribute++;
rgAttribute[reqInfo.cAttribute].pszObjId = szOID_OS_VERSION;
rgAttribute[reqInfo.cAttribute].cValue = 1;
rgAttribute[reqInfo.cAttribute].rgValue = &blobOSVAttr;
// put in the renewal cert if present
if(m_pCertContextRenewal != NULL && m_fHonorRenew) {
reqInfo.cAttribute++;
blobRenewAttr.pbData = m_pCertContextRenewal->pbCertEncoded;
blobRenewAttr.cbData = m_pCertContextRenewal->cbCertEncoded;
rgAttribute[reqInfo.cAttribute].pszObjId = szOID_RENEWAL_CERTIFICATE;
rgAttribute[reqInfo.cAttribute].cValue = 1;
rgAttribute[reqInfo.cAttribute].rgValue = &blobRenewAttr;
}
if (m_fNewRequestMethod && XECI_DISABLE != m_lClientId)
{
//put client id as attribute
hr = myEncodeRequestClientAttributeFromClientId(
m_lClientId,
&blobClientId.pbData,
&blobClientId.cbData);
if (S_OK != hr)
{
//for any reasons, don't include client ID
hr = put_ClientId(XECI_DISABLE);
if (S_OK != hr)
{
goto putClientIdError;
}
}
else
{
reqInfo.cAttribute++;
rgAttribute[reqInfo.cAttribute].pszObjId = szOID_REQUEST_CLIENT_INFO;
rgAttribute[reqInfo.cAttribute].cValue = 1;
rgAttribute[reqInfo.cAttribute].rgValue = &blobClientId;
}
}
// NOTE: On error we always return BAD ALGID
// this is because sometimes we get an no more data enum error
// that doesn't help.
// get the signature oid
if( !GetCapiHashAndSigAlgId(rgAlg) ) {
SetLastError((DWORD)NTE_BAD_ALGID);
goto ErrorGetCapiHashAndSigAlgId;
}
// Convert to an oid
if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
CRYPT_OID_INFO_SIGN_KEY,
(void *) rgAlg,
CRYPT_SIGN_ALG_OID_GROUP_ID)) ) ) {
SetLastError((DWORD)NTE_BAD_ALGID);
goto ErrorCryptFindOIDInfo;
}
// we always know we have at least 1 attribute, and we have been zero based, now go to 1 based.
reqInfo.cAttribute++;
SignatureInfo.SignatureAlgorithm.pszObjId = (char *) pOidInfo->pszOID;
#if DBG
//SignatureInfo.ToBeSigned.pbData should be null at the first
assert(NULL == SignatureInfo.ToBeSigned.pbData);
#endif
// encode PKCS10
while (TRUE)
{
if(!CryptEncodeObject(
CRYPT_ASN_ENCODING, X509_CERT_REQUEST_TO_BE_SIGNED,
&reqInfo,
SignatureInfo.ToBeSigned.pbData, // pbEncoded
&SignatureInfo.ToBeSigned.cbData))
{
goto ErrorEncodePKCS10ToBeSigned;
}
if (NULL != SignatureInfo.ToBeSigned.pbData)
{
//done
break;
}
SignatureInfo.ToBeSigned.pbData = (BYTE *)
LocalAlloc(LMEM_FIXED, SignatureInfo.ToBeSigned.cbData);
if (NULL == SignatureInfo.ToBeSigned.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
// create the signature Info
// Don't care if xchange or signature key in dwkeySpec because
// we are signing with the key that is in the PKCS10
#if DBG
assert(NULL == SignatureInfo.Signature.pbData);
#endif
while (TRUE)
{
if(!CryptSignCertificate(
hProv,
m_keyProvInfo.dwKeySpec,
CRYPT_ASN_ENCODING,
SignatureInfo.ToBeSigned.pbData,
SignatureInfo.ToBeSigned.cbData,
&SignatureInfo.SignatureAlgorithm,
NULL, // reserved
SignatureInfo.Signature.pbData, // pbSignature
&SignatureInfo.Signature.cbData))
{
goto ErrorCryptSignCertificatePKCS10;
}
if (NULL != SignatureInfo.Signature.pbData)
{
//done
break;
}
SignatureInfo.Signature.pbData = (BYTE *)
LocalAlloc(LMEM_FIXED, SignatureInfo.Signature.cbData);
if (NULL == SignatureInfo.Signature.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
// encode the final signed request
if( !CryptEncodeObject(
CRYPT_ASN_ENCODING,
X509_CERT,
&SignatureInfo,
NULL,
&pPkcs10Blob->cbData
) ||
(pPkcs10Blob->pbData = (BYTE *)
MyCoTaskMemAlloc(pPkcs10Blob->cbData)) == NULL ||
!CryptEncodeObject(
CRYPT_ASN_ENCODING,
X509_CERT,
&SignatureInfo,
pPkcs10Blob->pbData,
&pPkcs10Blob->cbData
) ) {
goto ErrorEncodePKCS10Request;
}
// go ahead and make the pkcs 7
if((m_pCertContextRenewal != NULL ||
m_pCertContextSigner != NULL) &&
m_fHonorRenew &&
!m_fCMCFormat) //if CMC, don't make pkcs7
{
// create a pkcs7 signed by the old cert
if(S_OK != CreatePKCS7RequestFromRequest(
pPkcs10Blob,
(NULL != m_pCertContextRenewal) ? m_pCertContextRenewal :
m_pCertContextSigner,
&blobPKCS7) )
goto ErrorCreatePKCS7RARequestFromPKCS10;
assert(pPkcs10Blob->pbData != NULL);
MyCoTaskMemFree(pPkcs10Blob->pbData);
*pPkcs10Blob = blobPKCS7;
memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
}
ssFlags = CERT_CREATE_SELFSIGN_NO_SIGN;
if(m_wszPVKFileName[0] != 0)
ssFlags |= CERT_CREATE_SELFSIGN_NO_KEY_INFO;
// Get the cert extensions we wish to add to the certificate.
// Search for the extensions we need.
{
PCERT_EXTENSION pCertExtCertTypeName = NULL;
while(NULL != (pCertExtCertTypeName = EnumStackExtension(pCertExtCertTypeName, m_fNewRequestMethod)) ) {
for (DWORD dTmp = 0; dTmp < sizeof(rgszExtensionOIDs) / sizeof(LPSTR); dTmp++) {
if (0 == strcmp(rgszExtensionOIDs[dTmp], pCertExtCertTypeName->pszObjId))
rgNeededExtensions[(ceExtensions.cExtension)++] = *pCertExtCertTypeName;
}
}
// Even if we didn't find all of the extensions we wanted, continue ...
}
assert(pCertContext == NULL);
pCertContext = MyCertCreateSelfSignCertificate(
hProv,
&reqInfo.Subject,
ssFlags,
&m_keyProvInfo,
NULL,
NULL,
NULL,
(ceExtensions.cExtension > 0) ? &ceExtensions : NULL
);
if (NULL == pCertContext)
goto ErrorCertCreateSelfSignCertificate;
// now put the pass thru data on the cert
requestFlags.fWriteToCSP = (m_fWriteCertToCSP != 0);
requestFlags.fWriteToDS = (m_fWriteCertToUserDS != 0);
requestFlags.openFlags = m_RequestStore.dwFlags;
#if DBG
assert(NULL == requestInfoBlob.pbData);
#endif
while (TRUE)
{
if(!CryptEncodeObject(
CRYPT_ASN_ENCODING,
XENROLL_REQUEST_INFO,
&requestFlags,
requestInfoBlob.pbData,
&requestInfoBlob.cbData))
{
goto ErrorEncodeRequestInfoBlob;
}
if (NULL != requestInfoBlob.pbData)
{
//done
break;
}
requestInfoBlob.pbData = (BYTE *)LocalAlloc(LMEM_FIXED, requestInfoBlob.cbData);
if (NULL == requestInfoBlob.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
// set the property on the dummy request cert.
if( !CertSetCertificateContextProperty(
pCertContext,
XENROLL_PASS_THRU_PROP_ID,
0,
&requestInfoBlob) )
goto ErrorCertSetCertificateContextProperty;
if(m_pCertContextRenewal != NULL && m_fHonorRenew) {
blobRenewalCert.pbData = m_pCertContextRenewal->pbCertEncoded;
blobRenewalCert.cbData = m_pCertContextRenewal->cbCertEncoded;
// set the renewal property if any
if( !CertSetCertificateContextProperty(
pCertContext,
XENROLL_RENEWAL_CERTIFICATE_PROP_ID,
0,
&blobRenewalCert) )
goto ErrorCertSetCertificateContextProperty;
}
// save the private key away if needed
if(m_wszPVKFileName[0] != 0) {
// open the PVK File
if( (hFile = CreateOpenFileSafely2(m_wszPVKFileName, IDS_PVK_C, IDS_PVK_O)) == NULL )
goto ErrorCreatePVKFile;
assert(m_keyProvInfo.dwKeySpec == AT_SIGNATURE || m_keyProvInfo.dwKeySpec == AT_KEYEXCHANGE);
// write out the private key
if( !PrivateKeySave(
hProv,
hFile,
m_keyProvInfo.dwKeySpec,
NULL,
m_wszPVKFileName,
0
) ) {
goto ErrorPrivateKeySave;
}
// put a different kind of propery in the store that just points to the pvk file
keyProvInfoT = m_keyProvInfo;
keyProvInfoT.pwszContainerName = m_wszPVKFileName;
if( !CreatePvkProperty(&keyProvInfoT, &blobData) )
goto ErrorCreatePvkProperty;
// This is really not needed, it is only nice for other tools
// like makecert or signcode to be able to look at a cert without
// specifying a .PVK file if the cert points to the .pvk file.
// So we don't care if this actually fail, which it will on Auth2 and
// SP3 Crypt32.dll since Phil was so kind as to not allow any unknown property
// to be set on the cert --- BAD PHIL!
CertSetCertificateContextProperty(
pCertContext,
CERT_PVK_FILE_PROP_ID,
0,
&blobData);
// only delete the keyset if the key was not pre-existing
// this is if we write it out to a PVK file only
// This is safe for scripting since we just generated this and we are putting it to
// a pvk file. We really aren't deleting the key.
if (!m_fNewRequestMethod)
{
//keep old behavior for createPKCS10 call
if(!m_fUseExistingKey)
GetProv(CRYPT_DELETEKEYSET);
}
}
//set all properties from the caller
pProp = EnumStackProperty(NULL);
while (NULL != pProp)
{
//goto request cert
if (!CertSetCertificateContextProperty(
pCertContext,
pProp->lPropId,
0,
&pProp->prop))
{
goto ErrorCertSetCertificateContextProperty;
}
pProp = EnumStackProperty(pProp);
}
// open the request cert store
if( (hStore = GetStore(StoreREQUEST)) == NULL)
goto ErrorCertOpenRequestStore;
//if old pending request exists, free it first
fRet = CertFreeCertificateContext(m_pCertContextPendingRequest);
#if DBG
assert(fRet);
#endif //DBG
m_pCertContextPendingRequest = NULL;
// save the temp cert
if( !MySafeCertAddCertificateContextToStore(
hStore,
pCertContext,
CERT_STORE_ADD_NEW,
&m_pCertContextPendingRequest,
m_dwEnabledSafteyOptions) ) {
goto ErrorCertAddToRequestStore;
}
// Remove the cached HASH.
if (m_hashBlobPendingRequest.pbData != NULL)
{
LocalFree(m_hashBlobPendingRequest.pbData);
m_hashBlobPendingRequest.pbData = NULL;
}
CommonReturn:
if(pCertContext != NULL)
CertFreeCertificateContext(pCertContext);
if(hFile != NULL)
CloseHandle(hFile);
if(blobData.pbData != NULL)
MyCoTaskMemFree(blobData.pbData);
if(blobPKCS7.pbData != NULL)
MyCoTaskMemFree(blobPKCS7.pbData);
if(CSPProvider.Signature.pbData)
LocalFree(CSPProvider.Signature.pbData);
if (NULL != pbSMime)
{
LocalFree(pbSMime);
}
if (NULL != reqInfo.Subject.pbData)
{
LocalFree(reqInfo.Subject.pbData);
}
if (NULL != rgExtension)
{
LocalFree(rgExtension);
}
if (NULL != pbKU)
{
LocalFree(pbKU);
}
if (NULL != pbEKU)
{
LocalFree(pbEKU);
}
if (NULL != pszPurpose)
{
LocalFree(pszPurpose);
}
if (NULL != enhKeyUsage.rgpszUsageIdentifier)
{
LocalFree(enhKeyUsage.rgpszUsageIdentifier);
}
if (NULL != blobExt.pbData)
{
LocalFree(blobExt.pbData);
}
if (NULL != blobCSPAttr.pbData)
{
LocalFree(blobCSPAttr.pbData);
}
if (NULL != blobOSVAttr.pbData)
{
LocalFree(blobOSVAttr.pbData);
}
if (NULL != SignatureInfo.ToBeSigned.pbData)
{
LocalFree(SignatureInfo.ToBeSigned.pbData);
}
if (NULL != SignatureInfo.Signature.pbData)
{
LocalFree(SignatureInfo.Signature.pbData);
}
if (NULL != requestInfoBlob.pbData)
{
LocalFree(requestInfoBlob.pbData);
}
if (NULL != blobClientId.pbData)
{
LocalFree(blobClientId.pbData);
}
if (NULL != pbSubjectKeyHashExtension)
{
LocalFree(pbSubjectKeyHashExtension);
}
if (NULL != pwszNotSafeRequesting)
{
LocalFree(pwszNotSafeRequesting);
}
if (NULL != pwszTitle)
{
LocalFree(pwszTitle);
}
// don't know if we have an error or not
// but I do know the errBefore is set properly
SetLastError(errBefore);
LeaveCriticalSection(&m_csXEnroll);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// We have an error, make sure we set it.
errBefore = GetLastError();
// on error return a NULL
if(pPkcs10Blob->pbData != NULL)
MyCoTaskMemFree(pPkcs10Blob->pbData);
memset(pPkcs10Blob, 0, sizeof(CRYPT_DATA_BLOB));
goto CommonReturn;
TRACE_ERROR(ErrorGetSignatureFromHPROV);
TRACE_ERROR(ErrorEncodeCSPAttr);
TRACE_ERROR(ErrorCertSetCertificateContextProperty);
TRACE_ERROR(ErrorCryptAcquireContext);
TRACE_ERROR(ErrorCryptGenKey);
TRACE_ERROR(ErrorCryptExportPublicKeyInfo);
TRACE_ERROR(ErrorCertStrToNameW);
TRACE_ERROR(ErrorEncodeKeyUsage);
TRACE_ERROR(ErrorEncodeEnhKeyUsage);
TRACE_ERROR(ErrorEncodeExtensions);
TRACE_ERROR(ErrorEncodePKCS10ToBeSigned);
TRACE_ERROR(ErrorCryptSignCertificatePKCS10);
TRACE_ERROR(ErrorEncodePKCS10Request);
TRACE_ERROR(ErrorCantConvertPurpose);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorCertAddToRequestStore);
TRACE_ERROR(ErrorCreatePVKFile);
TRACE_ERROR(ErrorPrivateKeySave);
TRACE_ERROR(ErrorCreatePvkProperty);
TRACE_ERROR(ErrorCertCreateSelfSignCertificate);
TRACE_ERROR(ErrorEncodeRequestInfoBlob);
TRACE_ERROR(ErrorCreatePKCS7RARequestFromPKCS10);
TRACE_ERROR(ErrorGetCapiHashAndSigAlgId);
TRACE_ERROR(ErrorCryptFindOIDInfo);
TRACE_ERROR(ErrorEncodeOSVAttr);
TRACE_ERROR(ErrorGetVersionEx);
TRACE_ERROR(CancelledError);
TRACE_ERROR(CreateSMimeExtensionError);
TRACE_ERROR(OutOfMemoryError);
TRACE_ERROR(putClientIdError);
TRACE_ERROR(myCreateSubjectKeyIdentifierExtensionError)
TRACE_ERROR(xeLoadRCStringError);
TRACE_ERROR(CryptGetUserKeyCancelError)
#if DBG
TRACE_ERROR(SetKeyContainerSecurityError)
#endif //DBG
}
HRESULT STDMETHODCALLTYPE CCEnroll::acceptPKCS7Blob(
/* [in] */ PCRYPT_DATA_BLOB pBlobPKCS7) {
HRESULT hr;
HRESULT hr2 = S_OK;
LONG dwKeySpec = 0;
PCCERT_CONTEXT pCertContextMy = NULL;
PCCERT_CONTEXT pCertContextRequest = NULL;
PCCERT_CONTEXT pCertContextEnd = NULL;
HANDLE hFile = NULL;
DWORD cb = 0;
HCRYPTPROV hProv = NULL;
HCRYPTKEY hKey = NULL;
HCERTSTORE hStoreDS = NULL;
HCERTSTORE hStoreRequest = NULL;
HCERTSTORE hStoreMy = NULL;
LPWSTR pwszTitle = NULL;
LPWSTR pwszNotSafeAccepting = NULL;
EnterCriticalSection(&m_csXEnroll);
// Accepting a request is not safe for scripting: pop up a warning dialog if called from script
if (0 != m_dwEnabledSafteyOptions) {
hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
if (S_OK != hr)
goto xeLoadRCStringError;
hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_ACCEPTING_CERT, &pwszNotSafeAccepting);
if (S_OK != hr)
goto xeLoadRCStringError;
if (IDYES != MessageBoxU(NULL, pwszNotSafeAccepting, pwszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2)) {
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
goto CancelledError;
}
}
// get the end entity cert
hr2 = GetEndEntityCert(pBlobPKCS7, TRUE, &pCertContextEnd);
if (S_OK != hr2 && XENROLL_E_CANNOT_ADD_ROOT_CERT != hr2)
{
hr = hr2;
goto ErrorGetEndEntityCert;
}
if(m_fDeleteRequestCert)
{
if ((hStoreRequest = GetStore(StoreREQUEST)) == NULL)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCertOpenRequestStore;
}
// check to see if this is in the request store
if ((pCertContextRequest = CertFindCertificateInStore(
hStoreRequest,
CRYPT_ASN_ENCODING,
0,
CERT_FIND_PUBLIC_KEY,
(void *) &pCertContextEnd->pCertInfo->SubjectPublicKeyInfo,
NULL)) != NULL)
{
CertDeleteCertificateFromStore(pCertContextRequest);
pCertContextRequest = NULL;
}
}
cb = 0;
// if the cert is to be written to the CSP,
// put it there but only if we have keys
if (m_fWriteCertToCSP &&
CertGetCertificateContextProperty(
pCertContextEnd,
CERT_KEY_PROV_INFO_PROP_ID, NULL, &cb))
{
if ((hProv = GetProv(0)) == NULL)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCryptAcquireContext;
}
// This can't fail
get_KeySpec(&dwKeySpec);
if (!CryptGetUserKey(
hProv,
dwKeySpec,
&hKey))
{
hKey = NULL;
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCryptGetUserKey;
}
// always attempt to write the cert to the csp
if (!CryptSetKeyParam(
hKey,
KP_CERTIFICATE,
pCertContextEnd->pbCertEncoded,
0))
{
// only return an error if it is a smart card error
// otherwise ignore the error
if (SCODE_FACILITY(GetLastError()) == FACILITY_SCARD)
{
//return error code from writing cert to csp
//important to save the error code before following clean up
hr = MY_HRESULT_FROM_WIN32(GetLastError());
//if can't write cert back to smartcard, remove the cert from my store
if ((hStoreMy = GetStore(StoreMY)) == NULL)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCertOpenMyStore;
}
// check to see if this is in the MY store
if ((pCertContextMy = CertFindCertificateInStore(
hStoreMy,
CRYPT_ASN_ENCODING,
0,
CERT_FIND_PUBLIC_KEY,
(void *) &pCertContextEnd->pCertInfo->SubjectPublicKeyInfo,
NULL)) != NULL)
{
//try to remove it
CertDeleteCertificateFromStore(pCertContextMy);
pCertContextMy = NULL;
}
if (!m_fUseExistingKey)
{
GetProv(CRYPT_DELETEKEYSET);
}
//error any way
goto ErrorWriteToCSP;
}
}
}
if(m_fWriteCertToUserDS)
{
// otherwise attempt to open the store
if ((hStoreDS = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
X509_ASN_ENCODING,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
L"UserDS")) == NULL)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCertOpenDSStore;
}
if (!CertAddCertificateContextToStore(
hStoreDS,
pCertContextEnd,
CERT_STORE_ADD_NEW,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorAddCertificateContextToDSStore;
}
CertCloseStore(hStoreDS, 0);
hStoreDS = NULL;
}
// determine if he wants to save the spc file
if (m_wszSPCFileName[0] != 0)
{
// open the spc file
hFile = CreateOpenFileSafely2(m_wszSPCFileName, IDS_SPC_C, IDS_SPC_O);
if (NULL == hFile)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorCreateSPCFile;
}
// write the spc
assert(pBlobPKCS7->pbData != NULL);
cb = 0;
if (!WriteFile(
hFile,
pBlobPKCS7->pbData,
pBlobPKCS7->cbData,
&cb,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto ErrorWriteSPCFile;
}
}
if (S_OK != hr2)
{
//return hr2 error
hr = hr2;
}
else
{
hr = S_OK;
}
ErrorReturn:
if(hKey != NULL)
CryptDestroyKey(hKey);
if(hFile != NULL)
CloseHandle(hFile);
if(pCertContextEnd != NULL)
CertFreeCertificateContext(pCertContextEnd);
if(hStoreDS != NULL)
CertCloseStore(hStoreDS, 0);
if (NULL != pwszNotSafeAccepting)
LocalFree(pwszNotSafeAccepting);
if (NULL != pwszTitle)
LocalFree(pwszTitle);
LeaveCriticalSection(&m_csXEnroll);
return(hr);
TRACE_ERROR(ErrorWriteToCSP);
TRACE_ERROR(ErrorCreateSPCFile);
TRACE_ERROR(ErrorWriteSPCFile);
TRACE_ERROR(ErrorGetEndEntityCert);
TRACE_ERROR(ErrorCryptAcquireContext);
TRACE_ERROR(ErrorCryptGetUserKey);
TRACE_ERROR(ErrorCertOpenDSStore);
TRACE_ERROR(ErrorAddCertificateContextToDSStore);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorCertOpenMyStore);
TRACE_ERROR(CancelledError);
TRACE_ERROR(xeLoadRCStringError);
}
PCCERT_CONTEXT STDMETHODCALLTYPE CCEnroll::getCertContextFromPKCS7(
/* [in] */ PCRYPT_DATA_BLOB pBlobPKCS7) {
HRESULT hr;
PCCERT_CONTEXT pCert;
// get the end entity cert
hr = GetEndEntityCert(pBlobPKCS7, FALSE, &pCert);
#if DBG
if (S_OK != hr)
{
assert(NULL == pCert);
}
#endif //DBG
return pCert;
}
HRESULT STDMETHODCALLTYPE CCEnroll::enumProvidersWStr(
/* [in] */ LONG dwIndex,
/* [in] */ LONG dwFlags,
/* [out] */ LPWSTR __RPC_FAR *ppwsz) {
DWORD iLast = 0;
LONG i;
DWORD dwProvType = 0;
DWORD cb = 0;
HRESULT hr = S_OK;
DWORD errBefore = GetLastError();
assert(ppwsz != NULL);
*ppwsz = NULL;
SetLastError(ERROR_SUCCESS);
EnterCriticalSection(&m_csXEnroll);
for(i=0; i<=dwIndex; i++) {
do {
cb = 0;
if( !CryptEnumProvidersU(
iLast,
0,
0,
&dwProvType,
NULL,
&cb
) ) {
// only skip if entry is bad
if( GetLastError() != NTE_PROV_TYPE_ENTRY_BAD)
goto ErrorCryptEnumProvidersU;
}
iLast++;
} while((CRYPT_ENUM_ALL_PROVIDERS & dwFlags) != CRYPT_ENUM_ALL_PROVIDERS &&
dwProvType != m_keyProvInfo.dwProvType);
}
iLast--;
if( (*ppwsz = (LPWSTR) MyCoTaskMemAlloc(cb)) == NULL ||
!CryptEnumProvidersU(
iLast,
0,
0,
&dwProvType,
*ppwsz,
&cb
) ) {
goto ErrorCryptEnumProvidersU;
}
CommonReturn:
SetLastError(errBefore);
LeaveCriticalSection(&m_csXEnroll);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// We have an error, make sure we set it.
errBefore = GetLastError();
if(*ppwsz != NULL)
{
MyCoTaskMemFree(*ppwsz);
*ppwsz = NULL;
}
goto CommonReturn;
TRACE_ERROR(ErrorCryptEnumProvidersU);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE CCEnroll::enumContainersWStr(
/* [in] */ LONG dwIndex,
/* [out] */ LPWSTR __RPC_FAR *ppwsz) {
DWORD errBefore = GetLastError();
DWORD cb = 0;
LONG i = 0;
char * psz = NULL;
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
SetLastError(ERROR_SUCCESS);
assert(ppwsz != NULL);
*ppwsz = NULL;
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
goto AccessDeniedError;
hr = GetVerifyProv();
if (S_OK != hr)
{
goto GetVerifyProvError;
}
while (TRUE)
{
if(!CryptGetProvParam(
m_hVerifyProv,
PP_ENUMCONTAINERS,
(BYTE*)psz,
&cb,
CRYPT_FIRST))
{
goto ErrorCryptGetProvParam;
}
if (NULL != psz)
{
//done
break;
}
psz = (char*)LocalAlloc(LMEM_FIXED, cb);
if (NULL == psz)
{
goto ErrorOutOfMem;
}
}
for(i=1; i<=dwIndex; i++) {
//assume 1st enum buffer size is big enough for all?
if( !CryptGetProvParam(
m_hVerifyProv,
PP_ENUMCONTAINERS,
(BYTE *) psz,
&cb,
0) )
goto ErrorCryptGetProvParam;
}
if( (*ppwsz = WideFromMB(psz)) == NULL )
goto ErrorOutOfMem;
CommonReturn:
if (NULL != psz)
{
LocalFree(psz);
}
SetLastError(errBefore);
LeaveCriticalSection(&m_csXEnroll);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// We have an error, make sure we set it.
errBefore = GetLastError();
if(*ppwsz != NULL)
MyCoTaskMemFree(*ppwsz);
*ppwsz = NULL;
goto CommonReturn;
SET_ERROR(AccessDeniedError, E_ACCESSDENIED);
TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
TRACE_ERROR(ErrorOutOfMem);
}
HRESULT CCEnroll::PKCS10ToCert(IN HCERTSTORE hCertStore,
IN CRYPT_DATA_BLOB pkcs10Blob,
OUT PCCERT_CONTEXT *ppCertContext)
{
HRESULT hr = E_FAIL;
PCERT_REQUEST_INFO pReqInfo = NULL;
// Input validation:
if (NULL == hCertStore || NULL == pkcs10Blob.pbData || NULL == ppCertContext)
return E_INVALIDARG;
if( !MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
&pkcs10Blob,
CERT_QUERY_CONTENT_FLAG_PKCS10,
CERT_QUERY_FORMAT_FLAG_ALL,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
NULL,
NULL,
NULL,
NULL,
(const void **) &pReqInfo) )
goto MyCryptQueryObjectError;
if ( NULL == (*ppCertContext = CertFindCertificateInStore
(hCertStore,
CRYPT_ASN_ENCODING,
0,
CERT_FIND_PUBLIC_KEY,
(void *) &pReqInfo->SubjectPublicKeyInfo,
NULL)) )
goto CertFindCertificateInStoreError;
hr = S_OK;
CommonReturn:
if (NULL != pReqInfo) { LocalFree(pReqInfo); } // Allocated in CryptQueryObject().
return hr;
ErrorReturn:
goto CommonReturn;
SET_HRESULT(CertFindCertificateInStoreError, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(MyCryptQueryObjectError, MY_HRESULT_FROM_WIN32(GetLastError()));
}
HRESULT CCEnroll::PKCS7ToCert(IN HCERTSTORE hCertStore,
IN CRYPT_DATA_BLOB pkcs7Blob,
OUT PCCERT_CONTEXT *ppCertContext)
{
CRYPT_DATA_BLOB pkcs10Blob;
CRYPT_VERIFY_MESSAGE_PARA VerifyPara;
HRESULT hr = E_FAIL;
// Init locals:
ZeroMemory(&pkcs10Blob, sizeof(pkcs10Blob));
ZeroMemory(&VerifyPara, sizeof(VerifyPara));
VerifyPara.cbSize = sizeof(VerifyPara);
VerifyPara.dwMsgAndCertEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
if (!MyCryptVerifyMessageSignature
(&VerifyPara,
0, // dwSignerIndex
pkcs7Blob.pbData,
pkcs7Blob.cbData,
pkcs10Blob.pbData,
&(pkcs10Blob.cbData),
NULL // ppSignerCert
) || 0 == pkcs10Blob.cbData)
goto MyCryptVerifyMessageSignatureError;
if (NULL == (pkcs10Blob.pbData = (PBYTE)LocalAlloc(LPTR, pkcs10Blob.cbData)))
goto MemoryError;
if (!MyCryptVerifyMessageSignature
(&VerifyPara,
0, // dwSignerIndex
pkcs7Blob.pbData,
pkcs7Blob.cbData,
pkcs10Blob.pbData,
&pkcs10Blob.cbData,
NULL // ppSignerCert
))
goto MyCryptVerifyMessageSignatureError;
hr = this->PKCS10ToCert(hCertStore, pkcs10Blob, ppCertContext);
CommonReturn:
if (NULL != pkcs10Blob.pbData) { LocalFree(pkcs10Blob.pbData); }
return hr;
ErrorReturn:
goto CommonReturn;
SET_HRESULT(MemoryError, E_OUTOFMEMORY);
SET_HRESULT(MyCryptVerifyMessageSignatureError, MY_HRESULT_FROM_WIN32(GetLastError()));
}
HRESULT STDMETHODCALLTYPE CCEnroll::freeRequestInfoBlob(
/* [in] */ CRYPT_DATA_BLOB pkcs7OrPkcs10) {
DWORD dwContentType = NULL;
HCERTSTORE hStoreRequest = NULL;
HRESULT hr = E_FAIL;
PCCERT_CONTEXT pCertContext = NULL;
// We're not supposed to delete the cert anyway, so we're done!
if (!m_fDeleteRequestCert)
return S_OK;
if (NULL == pkcs7OrPkcs10.pbData)
return E_INVALIDARG;
EnterCriticalSection(&m_csXEnroll);
// Step 1) Determine if we have a PKCS7 or a PKCS10:
//
if( !MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
&pkcs7OrPkcs10,
(CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED | CERT_QUERY_CONTENT_FLAG_PKCS10),
CERT_QUERY_FORMAT_FLAG_ALL,
0,
NULL,
&dwContentType, // OUT: PKCS10 or PKCS7
NULL,
NULL,
NULL,
NULL) )
goto MyCryptQueryObjectError;
// Step 2) Find a cert context with a matching public key in the request store:
//
if (NULL == (hStoreRequest = GetStore(StoreREQUEST)))
goto UnexpectedError;
switch (dwContentType)
{
case CERT_QUERY_CONTENT_PKCS7_SIGNED:
hr = this->PKCS7ToCert(hStoreRequest, pkcs7OrPkcs10, &pCertContext);
if (S_OK != hr)
{
if (CRYPT_E_NOT_FOUND == hr)
{
//freeRequestInfo could be called when cert is not issued
//PKCS7 could be CMC which is signed by request key and
//cert is not in local store yet. We try cached cert
if (NULL != m_pCertContextPendingRequest)
{
//looks we still have cached request cert handle
pCertContext = CertDuplicateCertificateContext(
m_pCertContextPendingRequest);
if (NULL == pCertContext)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertDuplicateCertificateContextError;
}
}
else if (NULL != m_hashBlobPendingRequest.pbData &&
0 < m_hashBlobPendingRequest.cbData)
{
//don't have cached request handle but thumbprint exists
//retrieve the request cert handle from store
pCertContext = CertFindCertificateInStore(
hStoreRequest, //request store
X509_ASN_ENCODING,
0,
CERT_FIND_HASH,
&m_hashBlobPendingRequest,
NULL);
if (NULL == pCertContext)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertFindCertificateInStoreError;
}
}
else
{
//sorry, don't know which cert to be free
//however, can try to find public key from PKCS7
goto PKCS7ToCertError;
}
}
else
{
//other errors
goto PKCS7ToCertError;
}
}
break;
case CERT_QUERY_CONTENT_PKCS10:
if (S_OK != (hr = this->PKCS10ToCert(hStoreRequest, pkcs7OrPkcs10, &pCertContext)))
goto PKCS10ToCertError;
break;
default:
goto InvalidContentTypeError;
}
if (!CertDeleteCertificateFromStore(pCertContext))
{
// pCertContext is freed even when CertDeleteCertificateFromStore() returns an error.
pCertContext = NULL;
goto CertDeleteCertificateFromStoreError;
}
hr = S_OK;
CommonReturn:
LeaveCriticalSection(&m_csXEnroll);
return hr;
ErrorReturn:
if (NULL != pCertContext) { CertFreeCertificateContext(pCertContext); }
goto CommonReturn;
SET_HRESULT(CertDeleteCertificateFromStoreError, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(PKCS7ToCertError, hr);
SET_HRESULT(PKCS10ToCertError, hr);
SET_HRESULT(InvalidContentTypeError, E_INVALIDARG);
SET_HRESULT(MyCryptQueryObjectError, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(UnexpectedError, E_UNEXPECTED);
TRACE_ERROR(CertDuplicateCertificateContextError)
TRACE_ERROR(CertFindCertificateInStoreError)
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_SPCFileNameWStr(
/* [out] */ LPWSTR __RPC_FAR *szw) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*szw = CopyWideString(m_wszSPCFileName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_SPCFileNameWStr(
/* [in] */ LPWSTR pwsz) {
HRESULT hr = S_OK;
if(pwsz == NULL)
return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
EnterCriticalSection(&m_csXEnroll);
if( m_wszSPCFileName != wszEmpty)
MyCoTaskMemFree(m_wszSPCFileName);
if( (m_wszSPCFileName = CopyWideString(pwsz)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_PVKFileNameWStr(
/* [out] */ LPWSTR __RPC_FAR *szw) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if( (*szw = CopyWideString(m_wszPVKFileName)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_PVKFileNameWStr(
/* [in] */ LPWSTR pwsz) {
HRESULT hr = S_OK;
if(pwsz == NULL)
return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
EnterCriticalSection(&m_csXEnroll);
if( m_wszPVKFileName != wszEmpty)
MyCoTaskMemFree(m_wszPVKFileName);
if( (m_wszPVKFileName = CopyWideString(pwsz)) == NULL )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
else
m_dwGenKeyFlags |= CRYPT_EXPORTABLE; //why???
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_HashAlgorithmWStr(
/* [out] */ LPWSTR __RPC_FAR *ppwsz) {
PCCRYPT_OID_INFO pOidInfo = NULL;
ALG_ID rgAlg[2];
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
assert(ppwsz != NULL);
*ppwsz = NULL;
if( !GetCapiHashAndSigAlgId(rgAlg) )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// Convert to an oid
else if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
CRYPT_OID_INFO_ALGID_KEY,
(void *) &rgAlg[0],
CRYPT_HASH_ALG_OID_GROUP_ID)) ) ) {
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
else if( (*ppwsz = WideFromMB(pOidInfo->pszOID)) == NULL)
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_HashAlgorithmWStr(
/* [in] */ LPWSTR pwsz) {
HRESULT hr = S_OK;
char * szObjId = NULL;
PCCRYPT_OID_INFO pOidInfo = NULL;
if(pwsz == NULL) {
return(MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
}
if(!_wcsicmp(L"SHA1", pwsz))
szObjId = CopyAsciiString(szOID_OIWSEC_sha1);
else if(!_wcsicmp(L"MD5", pwsz))
szObjId = CopyAsciiString(szOID_RSA_MD5RSA);
else if(!_wcsicmp(L"MD2", pwsz))
szObjId = CopyAsciiString(szOID_RSA_MD2RSA);
else
szObjId = MBFromWide(pwsz);
// something went wrong
if(szObjId == NULL)
return(MY_HRESULT_FROM_WIN32(GetLastError()));
// find the hashing algid
if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
CRYPT_OID_INFO_OID_KEY,
szObjId,
0)) ) )
{
//XIAOHS: CryptFindOIDInfo does not set the LastError in this case.
//AV in xEnroll. See bug# 189320
//hr = MY_HRESULT_FROM_WIN32(GetLastError());
hr=NTE_BAD_ALGID;
}
assert(szObjId != NULL);
MyCoTaskMemFree(szObjId);
EnterCriticalSection(&m_csXEnroll);
if(hr == S_OK) {
if( pOidInfo->dwGroupId == CRYPT_HASH_ALG_OID_GROUP_ID ||
pOidInfo->dwGroupId == CRYPT_SIGN_ALG_OID_GROUP_ID )
m_HashAlgId = pOidInfo->Algid;
else
hr = CRYPT_E_NOT_FOUND;
}
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_HashAlgID(
LONG hashAlgID
) {
EnterCriticalSection(&m_csXEnroll);
m_HashAlgId = hashAlgID;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_HashAlgID(
LONG * hashAlgID
) {
EnterCriticalSection(&m_csXEnroll);
*hashAlgID = m_HashAlgId;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_RenewalCertificate(
/* [out] */ PCCERT_CONTEXT __RPC_FAR *ppCertContext) {
HRESULT hr = S_OK;
*ppCertContext = NULL;
if( m_pCertContextRenewal == NULL)
return(MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND));
EnterCriticalSection(&m_csXEnroll);
if( NULL == (*ppCertContext = CertDuplicateCertificateContext(m_pCertContextRenewal)) )
hr = MY_HRESULT_FROM_WIN32(GetLastError());
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_RenewalCertificate(
/* [in] */ PCCERT_CONTEXT pCertContext)
{
HRESULT hr;
PCCERT_CONTEXT pGoodCertContext= NULL;
EnterCriticalSection(&m_csXEnroll);
hr = GetGoodCertContext(pCertContext, &pGoodCertContext);
if (S_OK != hr)
{
goto GetGoodCertContextError;
}
if(m_pCertContextRenewal != NULL)
{
CertFreeCertificateContext(m_pCertContextRenewal);
}
m_pCertContextRenewal = pGoodCertContext;
hr = S_OK;
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return hr;
TRACE_ERROR(GetGoodCertContextError);
}
BOOL
CCEnroll::CopyAndPushStackExtension(
PCERT_EXTENSION pExt,
BOOL fCMC)
{
DWORD cb = 0;
DWORD cbOid = 0;
PEXT_STACK pExtStackEle = NULL;
PBYTE pb = NULL;
PEXT_STACK *ppExtStack = NULL;
DWORD *pcExtStack = NULL;
assert(pExt != NULL);
// allocate the space
cbOid = POINTERROUND((DWORD)strlen(pExt->pszObjId) + 1); //ia64 align
cb = sizeof(EXT_STACK) + cbOid + pExt->Value.cbData;
if(NULL == (pb = (PBYTE) malloc(cb))) {
SetLastError(ERROR_OUTOFMEMORY);
return(FALSE);
}
// set my pointers
pExtStackEle = (PEXT_STACK) pb;
pb += sizeof(EXT_STACK);
pExtStackEle->ext.pszObjId = (LPSTR) pb;
pb += cbOid;
pExtStackEle->ext.Value.pbData = pb;
// set the values
strcpy(pExtStackEle->ext.pszObjId, pExt->pszObjId);
pExtStackEle->ext.fCritical = pExt->fCritical;
pExtStackEle->ext.Value.cbData = pExt->Value.cbData;
memcpy(pExtStackEle->ext.Value.pbData, pExt->Value.pbData, pExt->Value.cbData);
// insert on the list
EnterCriticalSection(&m_csXEnroll);
ppExtStack = fCMC ? &m_pExtStackNew : &m_pExtStack;
pcExtStack = fCMC ? &m_cExtStackNew : &m_cExtStack;
pExtStackEle->pNext = *ppExtStack;
*ppExtStack = pExtStackEle;
(*pcExtStack)++;
LeaveCriticalSection(&m_csXEnroll);
return(TRUE);
}
PCERT_EXTENSION
CCEnroll::PopStackExtension(
BOOL fCMC)
{
PEXT_STACK pExtStackEle = NULL;
PEXT_STACK *ppExtStack = NULL;
DWORD *pcExtStack = NULL;
EnterCriticalSection(&m_csXEnroll);
ppExtStack = fCMC ? &m_pExtStackNew : &m_pExtStack;
if(NULL != *ppExtStack)
{
pExtStackEle = *ppExtStack;
*ppExtStack = (*ppExtStack)->pNext;
pcExtStack = fCMC ? &m_cExtStackNew : &m_cExtStack;
(*pcExtStack)--;
}
LeaveCriticalSection(&m_csXEnroll);
return((PCERT_EXTENSION) pExtStackEle);
}
DWORD
CCEnroll::CountStackExtension(BOOL fCMC)
{
DWORD cExt = 0;
EnterCriticalSection(&m_csXEnroll);
cExt = fCMC ? m_cExtStackNew : m_cExtStack;
LeaveCriticalSection(&m_csXEnroll);
return(cExt);
}
PCERT_EXTENSION
CCEnroll::EnumStackExtension(
PCERT_EXTENSION pExtLast,
BOOL fCMC)
{
PEXT_STACK pExtStackEle = (PEXT_STACK)pExtLast;
EnterCriticalSection(&m_csXEnroll);
if(NULL == pExtStackEle)
{
pExtStackEle = fCMC ? m_pExtStackNew : m_pExtStack;
}
else
{
pExtStackEle = pExtStackEle->pNext;
}
LeaveCriticalSection(&m_csXEnroll);
return((PCERT_EXTENSION) pExtStackEle);
}
void
CCEnroll::FreeAllStackExtension(void)
{
EnterCriticalSection(&m_csXEnroll);
//free cmc extensions
while(0 != m_cExtStackNew)
{
FreeStackExtension(PopStackExtension(TRUE));
}
//free old client extensions
while(0 != m_cExtStack)
{
FreeStackExtension(PopStackExtension(FALSE));
}
LeaveCriticalSection(&m_csXEnroll);
}
void CCEnroll::FreeStackExtension(PCERT_EXTENSION pExt) {
if(pExt != NULL)
free(pExt);
}
//obselete call for new client
HRESULT STDMETHODCALLTYPE
CCEnroll::AddExtensionsToRequest(
/* [in] */ PCERT_EXTENSIONS pCertExtensions)
{
HRESULT hr = S_OK;
DWORD i = 0;
assert(pCertExtensions != NULL);
for(i = 0; i < pCertExtensions->cExtension; i++)
{
//push into old extension stack
if(!CopyAndPushStackExtension(&pCertExtensions->rgExtension[i], FALSE))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
break;
}
}
return(hr);
}
BOOL
CCEnroll::CopyAndPushStackAttribute(
PCRYPT_ATTRIBUTE pAttr,
BOOL fCMC)
{
DWORD i = 0;
DWORD cb = 0;
DWORD cbOid = 0;
PATTR_STACK pAttrStackEle = NULL;
PBYTE pb = NULL;
PATTR_STACK *ppAttrStack = NULL;
DWORD *pcAttrStack = NULL;
assert(pAttr != NULL);
// allocate the space
cb = sizeof(ATTR_STACK);
//make sure aligned for ia64
cbOid = POINTERROUND((DWORD)strlen(pAttr->pszObjId) + 1);
cb += cbOid;
cb += sizeof(CRYPT_ATTR_BLOB) * pAttr->cValue;
for(i=0; i<pAttr->cValue; i++)
cb += POINTERROUND(pAttr->rgValue[i].cbData); //pointer align
if(NULL == (pb = (PBYTE) malloc(cb))) {
SetLastError(ERROR_OUTOFMEMORY);
return(FALSE);
}
// set my pointers
pAttrStackEle = (PATTR_STACK) pb;
pb += sizeof(ATTR_STACK);
pAttrStackEle->attr.pszObjId = (LPSTR) pb;
pb += cbOid;
strcpy(pAttrStackEle->attr.pszObjId, pAttr->pszObjId);
pAttrStackEle->attr.cValue = pAttr->cValue;
pAttrStackEle->attr.rgValue = (PCRYPT_ATTR_BLOB) pb;
pb += sizeof(CRYPT_ATTR_BLOB) * pAttr->cValue;
for(i=0; i<pAttr->cValue; i++) {
pAttrStackEle->attr.rgValue[i].pbData = pb;
pAttrStackEle->attr.rgValue[i].cbData = pAttr->rgValue[i].cbData;
memcpy(pAttrStackEle->attr.rgValue[i].pbData, pAttr->rgValue[i].pbData, pAttr->rgValue[i].cbData);
pb += POINTERROUND(pAttr->rgValue[i].cbData);
}
assert( pb == ((BYTE *) pAttrStackEle) + cb );
// insert on the list
EnterCriticalSection(&m_csXEnroll);
ppAttrStack = fCMC ? &m_pAttrStackNew : &m_pAttrStack;
pcAttrStack = fCMC ? &m_cAttrStackNew : &m_cAttrStack;
pAttrStackEle->pNext = *ppAttrStack;
*ppAttrStack = pAttrStackEle;
(*pcAttrStack)++;
LeaveCriticalSection(&m_csXEnroll);
return(TRUE);
}
PCRYPT_ATTRIBUTE
CCEnroll::PopStackAttribute(BOOL fCMC)
{
PATTR_STACK pAttrStackEle = NULL;
PATTR_STACK *ppAttrStack = NULL;
DWORD *pcAttrStack = NULL;
EnterCriticalSection(&m_csXEnroll);
ppAttrStack = fCMC ? &m_pAttrStackNew : &m_pAttrStack;
if(NULL != *ppAttrStack)
{
pAttrStackEle = *ppAttrStack;
*ppAttrStack = (*ppAttrStack)->pNext;
pcAttrStack = fCMC ? &m_cAttrStackNew : &m_cAttrStack;
(*pcAttrStack)--;
}
LeaveCriticalSection(&m_csXEnroll);
return((PCRYPT_ATTRIBUTE) pAttrStackEle);
}
DWORD
CCEnroll::CountStackAttribute(BOOL fCMC)
{
DWORD cAttr = 0;
EnterCriticalSection(&m_csXEnroll);
cAttr = fCMC ? m_cAttrStackNew : m_cAttrStack;
LeaveCriticalSection(&m_csXEnroll);
return(cAttr);
}
PCRYPT_ATTRIBUTE
CCEnroll::EnumStackAttribute(
PCRYPT_ATTRIBUTE pAttrLast,
BOOL fCMC)
{
PATTR_STACK pAttrStackEle = (PATTR_STACK) pAttrLast;
EnterCriticalSection(&m_csXEnroll);
if(NULL == pAttrLast)
{
pAttrStackEle = fCMC ? m_pAttrStackNew : m_pAttrStack;
}
else
{
pAttrStackEle = pAttrStackEle->pNext;
}
LeaveCriticalSection(&m_csXEnroll);
return((PCRYPT_ATTRIBUTE) pAttrStackEle);
}
void CCEnroll::FreeAllStackAttribute(void)
{
EnterCriticalSection(&m_csXEnroll);
while(0 != m_cAttrStackNew)
{
FreeStackAttribute(PopStackAttribute(TRUE));
}
while(0 != m_cAttrStack)
{
FreeStackAttribute(PopStackAttribute(FALSE));
}
LeaveCriticalSection(&m_csXEnroll);
}
void CCEnroll::FreeStackAttribute(PCRYPT_ATTRIBUTE pAttr) {
if(pAttr != NULL)
free(pAttr);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::AddAuthenticatedAttributesToPKCS7Request(
/* [in] */ PCRYPT_ATTRIBUTES pAttributes)
{
HRESULT hr = S_OK;
DWORD i;
for(i = 0; i < pAttributes->cAttr; i++)
{
if(!CopyAndPushStackAttribute(&pAttributes->rgAttr[i], FALSE))
{
hr = (MY_HRESULT_FROM_WIN32(GetLastError()));
break;
}
//put into cmc stack too
if(!CopyAndPushStackAttribute(&pAttributes->rgAttr[i], TRUE))
{
hr = (MY_HRESULT_FROM_WIN32(GetLastError()));
break;
}
}
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::CreatePKCS7RequestFromRequest(
/* [in] */ PCRYPT_DATA_BLOB pRequest,
/* [in] */ PCCERT_CONTEXT pSigningRACertContext,
/* [out] */ PCRYPT_DATA_BLOB pPkcs7Blob) {
HRESULT hr = S_OK;
DWORD errBefore = GetLastError();
CRYPT_SIGN_MESSAGE_PARA signMsgPara;
PCCRYPT_OID_INFO pOidInfo = NULL;
PCRYPT_ATTRIBUTE pAttrCur = NULL;
DWORD i;
ALG_ID rgAlg[2];
CRYPT_KEY_PROV_INFO *pKeyProvInfo = NULL;
DWORD cb = 0;
assert(pSigningRACertContext != NULL);
assert(pRequest != NULL);
assert(pPkcs7Blob != NULL);
memset(&signMsgPara, 0, sizeof(CRYPT_SIGN_MESSAGE_PARA));
memset(pPkcs7Blob, 0, sizeof(CRYPT_DATA_BLOB));
if( !GetCapiHashAndSigAlgId(rgAlg) )
goto ErrorGetCapiHashAndSigAlgId;
// find out what the oid is
if( (NULL == (pOidInfo = xeCryptFindOIDInfo(
CRYPT_OID_INFO_ALGID_KEY,
(void *) &rgAlg[0],
CRYPT_HASH_ALG_OID_GROUP_ID)) ) )
{
SetLastError((DWORD)NTE_BAD_ALGID);
goto ErrorCryptFindOIDInfo;
}
// now add all of the user defined extensions
EnterCriticalSection(&m_csXEnroll);
signMsgPara.cAuthAttr = CountStackAttribute(m_fNewRequestMethod);
signMsgPara.rgAuthAttr = (PCRYPT_ATTRIBUTE)LocalAlloc(LMEM_FIXED,
signMsgPara.cAuthAttr * sizeof(CRYPT_ATTRIBUTE));
if( NULL == signMsgPara.rgAuthAttr)
{
SetLastError(ERROR_OUTOFMEMORY);
LeaveCriticalSection(&m_csXEnroll);
goto ErrorOutOfMemory;
}
i = 0;
pAttrCur = NULL;
while(NULL != (pAttrCur = EnumStackAttribute(pAttrCur, m_fNewRequestMethod)) ) {
signMsgPara.rgAuthAttr[i] = *pAttrCur;
i++;
}
LeaveCriticalSection(&m_csXEnroll);
signMsgPara.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
signMsgPara.dwMsgEncodingType = PKCS_7_ASN_ENCODING;
signMsgPara.pSigningCert = pSigningRACertContext;
signMsgPara.HashAlgorithm.pszObjId = (char *) pOidInfo->pszOID;
signMsgPara.cMsgCert = 1;
signMsgPara.rgpMsgCert = &pSigningRACertContext;
//get key prov info
while (TRUE)
{
if(!CertGetCertificateContextProperty(
pSigningRACertContext,
CERT_KEY_PROV_INFO_PROP_ID,
pKeyProvInfo,
&cb))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertGetCertificateContextPropertyError;
}
if (NULL != pKeyProvInfo)
{
//got it, done
break;
}
pKeyProvInfo = (CRYPT_KEY_PROV_INFO*)LocalAlloc(LMEM_FIXED, cb);
if (NULL == pKeyProvInfo)
{
hr = E_OUTOFMEMORY;
goto ErrorOutOfMemory;
}
}
if (0x0 != (pKeyProvInfo->dwFlags & CRYPT_SILENT))
{
//have to set silent through msg param
signMsgPara.dwFlags |= CRYPT_MESSAGE_SILENT_KEYSET_FLAG;
}
if( !CryptSignMessage(
&signMsgPara,
FALSE,
1,
(const BYTE **) &pRequest->pbData,
&pRequest->cbData ,
NULL,
&pPkcs7Blob->cbData) ||
(pPkcs7Blob->pbData = (BYTE *)
MyCoTaskMemAlloc(pPkcs7Blob->cbData)) == NULL ||
!CryptSignMessage(
&signMsgPara,
FALSE,
1,
(const BYTE **) &pRequest->pbData,
&pRequest->cbData ,
pPkcs7Blob->pbData,
&pPkcs7Blob->cbData) )
goto ErrorCryptSignMessage;
CommonReturn:
if (NULL != pKeyProvInfo)
{
LocalFree(pKeyProvInfo);
}
if (NULL != signMsgPara.rgAuthAttr)
{
LocalFree(signMsgPara.rgAuthAttr);
}
// don't know if we have an error or not
// but I do know the errBefore is set properly
SetLastError(errBefore);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// We have an error, make sure we set it.
errBefore = GetLastError();
// on error return a NULL
if(pPkcs7Blob->pbData != NULL)
MyCoTaskMemFree(pPkcs7Blob->pbData);
memset(pPkcs7Blob, 0, sizeof(CRYPT_DATA_BLOB));
goto CommonReturn;
TRACE_ERROR(ErrorGetCapiHashAndSigAlgId);
TRACE_ERROR(ErrorCryptSignMessage);
TRACE_ERROR(ErrorCryptFindOIDInfo);
TRACE_ERROR(ErrorOutOfMemory);
TRACE_ERROR(CertGetCertificateContextPropertyError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::AddNameValuePairToSignatureWStr(
/* [in] */ LPWSTR pwszName,
/* [in] */ LPWSTR pwszValue)
{
HRESULT hr = S_OK;
assert(pwszName != NULL && pwszValue != NULL);
CRYPT_ENROLLMENT_NAME_VALUE_PAIR nameValuePair = {pwszName, pwszValue};
CRYPT_ATTR_BLOB blobAttr;
CRYPT_ATTRIBUTE attr = {szOID_ENROLLMENT_NAME_VALUE_PAIR, 1, &blobAttr};
CRYPT_ATTRIBUTES attrs = {1, &attr};
memset(&blobAttr, 0, sizeof(CRYPT_ATTR_BLOB));
hr = xeEncodeNameValuePair(
&nameValuePair,
&blobAttr.pbData,
&blobAttr.cbData);
if (S_OK != hr)
{
goto error;
}
hr = AddAuthenticatedAttributesToPKCS7Request(&attrs);
error:
if (NULL != blobAttr.pbData)
{
MyCoTaskMemFree(blobAttr.pbData);
}
return hr;
}
HRESULT STDMETHODCALLTYPE CCEnroll::AddCertTypeToRequestWStr(
LPWSTR szw) {
HRESULT hr = S_OK;
DWORD errBefore = GetLastError();
CERT_NAME_VALUE nameValue;
CERT_EXTENSION ext;
CERT_EXTENSIONS exts = {1, &ext};
memset(&ext, 0, sizeof(CERT_EXTENSION));
nameValue.dwValueType = CERT_RDN_BMP_STRING;
nameValue.Value.cbData = 0;
nameValue.Value.pbData = (PBYTE) szw;
ext.pszObjId = szOID_ENROLL_CERTTYPE_EXTENSION;
if( !CryptEncodeObject(
CRYPT_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
&nameValue,
NULL,
&ext.Value.cbData
) )
goto ErrorCryptEncodeObject;
ext.Value.pbData = (PBYTE)LocalAlloc(LMEM_FIXED, ext.Value.cbData);
if(NULL == ext.Value.pbData)
{
SetLastError(ERROR_OUTOFMEMORY);
goto ErrorOutOfMemory;
}
if( !CryptEncodeObject(
CRYPT_ASN_ENCODING,
X509_UNICODE_ANY_STRING,
&nameValue,
ext.Value.pbData,
&ext.Value.cbData
) )
goto ErrorCryptEncodeObject;
if(S_OK != AddExtensionsToRequest(&exts))
goto ErrorAddExtensionsToRequest;
//put cert template extension into CMC stack
if(!CopyAndPushStackExtension(&ext, TRUE))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CopyAndPushStackExtensionError;
}
CommonReturn:
if (NULL != ext.Value.pbData)
{
LocalFree(ext.Value.pbData);
}
// don't know if we have an error or not
// but I do know the errBefore is set properly
SetLastError(errBefore);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// We have an error, make sure we set it.
errBefore = GetLastError();
goto CommonReturn;
TRACE_ERROR(ErrorCryptEncodeObject);
TRACE_ERROR(ErrorAddExtensionsToRequest);
TRACE_ERROR(ErrorOutOfMemory);
TRACE_ERROR(CopyAndPushStackExtensionError);
}
HRESULT STDMETHODCALLTYPE CCEnroll::AddCertTypeToRequestWStrEx(
IN LONG lType,
IN LPCWSTR pwszOIDOrName,
IN LONG lMajorVersion,
IN BOOL fMinorVersion,
IN LONG lMinorVersion)
{
HRESULT hr;
LPCSTR lpszStructType;
CERT_NAME_VALUE nameValue;
CERT_TEMPLATE_EXT Template;
VOID *pv;
CERT_EXTENSION ext; //free pbData
DWORD cb = 0;
CHAR *pszOID = NULL;
//init
ZeroMemory(&ext, sizeof(ext));
ext.fCritical = FALSE;
if (NULL == pwszOIDOrName)
{
hr = E_INVALIDARG;
goto InvalidArgError;
}
switch (lType)
{
case XECT_EXTENSION_V1:
ext.pszObjId = szOID_ENROLL_CERTTYPE_EXTENSION;
nameValue.dwValueType = CERT_RDN_BMP_STRING;
nameValue.Value.cbData = 0;
nameValue.Value.pbData = (BYTE*)pwszOIDOrName;
pv = (VOID*)&nameValue;
lpszStructType = X509_UNICODE_ANY_STRING;
break;
case XECT_EXTENSION_V2:
ext.pszObjId = szOID_CERTIFICATE_TEMPLATE;
//convert wsz OID to ansi
while (TRUE)
{
cb = WideCharToMultiByte(
GetACP(),
0,
pwszOIDOrName,
-1,
pszOID,
cb,
NULL,
NULL);
if (0 == cb)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto WideCharToMultiByteError;
}
if (NULL != pszOID)
{
//done
break;
}
pszOID = (CHAR*)LocalAlloc(LMEM_FIXED, cb);
if (NULL == pszOID)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
ZeroMemory(&Template, sizeof(Template));
Template.pszObjId = pszOID;
Template.dwMajorVersion = lMajorVersion;
Template.fMinorVersion = fMinorVersion;
Template.dwMinorVersion = lMinorVersion;
pv = (VOID*)&Template;
lpszStructType = X509_CERTIFICATE_TEMPLATE;
break;
default:
hr = E_INVALIDARG;
goto InvalidArgError;
break;
}
while (TRUE)
{
if (!CryptEncodeObject(
X509_ASN_ENCODING,
lpszStructType,
pv,
ext.Value.pbData,
&ext.Value.cbData))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptEncodeObjectError;
}
if (NULL != ext.Value.pbData)
{
//done
break;
}
ext.Value.pbData = (BYTE*)LocalAlloc(LMEM_FIXED, ext.Value.cbData);
if (NULL == ext.Value.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
//put cert template extension into CMC stack
if(!CopyAndPushStackExtension(&ext, TRUE))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CopyAndPushStackExtensionError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pszOID)
{
LocalFree(pszOID);
}
if (NULL != ext.Value.pbData)
{
LocalFree(ext.Value.pbData);
}
return hr;
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(CopyAndPushStackExtensionError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptEncodeObjectError)
TRACE_ERROR(WideCharToMultiByteError)
}
HRESULT STDMETHODCALLTYPE CCEnroll::getProviderTypeWStr(
IN LPCWSTR pwszProvName,
OUT LONG * plProvType)
{
HRESULT hr;
DWORD i = 0;
DWORD cb;
DWORD dwProvType;
WCHAR *pwszEnumProvName = NULL;
if (NULL == pwszProvName)
{
hr = E_INVALIDARG;
goto InvalidArgError;
}
//init
*plProvType = -1;
while (TRUE)
{
while (TRUE)
{
if (!CryptEnumProvidersU(
i,
NULL,
0,
&dwProvType,
pwszEnumProvName,
&cb))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
if (MY_HRESULT_FROM_WIN32(NTE_PROV_TYPE_ENTRY_BAD) == hr)
{
//skip bad one and goto next
assert(NULL == pwszEnumProvName);
break; //skip this one
}
//error
goto CryptEnumProvidersUError;
}
if (NULL != pwszEnumProvName)
{
//get the current csp name
break;
}
pwszEnumProvName = (WCHAR*)LocalAlloc(LMEM_FIXED, cb);
if (NULL == pwszEnumProvName)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
if (NULL != pwszEnumProvName)
{
if (0 == _wcsicmp(pwszProvName, pwszEnumProvName))
{
//found matched name
*plProvType = (LONG)dwProvType;
break; //out of outer loop
}
}
//not mached, go to next one
++i;
if (NULL != pwszEnumProvName)
{
LocalFree(pwszEnumProvName);
pwszEnumProvName = NULL;
}
}
hr = S_OK;
ErrorReturn:
if (NULL != pwszEnumProvName)
{
LocalFree(pwszEnumProvName);
}
return hr;
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptEnumProvidersUError)
}
HRESULT STDMETHODCALLTYPE CCEnroll::InstallPKCS7Blob(
/* [in] */ PCRYPT_DATA_BLOB pBlobPKCS7)
{
return InstallPKCS7BlobEx(pBlobPKCS7, NULL);
}
HRESULT CCEnroll::InstallPKCS7BlobEx(
/* [in] */ PCRYPT_DATA_BLOB pBlobPKCS7,
/* [out] */ LONG *plCertInstalled)
{
HRESULT hr = S_OK;
DWORD errBefore = GetLastError();
HCERTSTORE hStoreMsg = NULL;
LPWSTR pwszTitle = NULL;
LPWSTR pwszNotSafeAccepting = NULL;
EnterCriticalSection(&m_csXEnroll);
// Accepting a request is not safe for scripting: pop up a warning dialog if called from script
if (0 != m_dwEnabledSafteyOptions) {
hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFEACTION, &pwszTitle);
if (S_OK != hr) {
SetLastError(hr);
goto xeLoadRCStringError;
}
hr = xeLoadRCString(hInstanceXEnroll, IDS_NOTSAFE_ACCEPTING_CERT, &pwszNotSafeAccepting);
if (S_OK != hr) {
SetLastError(hr);
goto xeLoadRCStringError;
}
if (IDYES != MessageBoxU(NULL, pwszNotSafeAccepting, pwszTitle, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2)) {
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
SetLastError(hr);
goto CancelledError;
}
}
if( !MyCryptQueryObject(CERT_QUERY_OBJECT_BLOB,
pBlobPKCS7,
(CERT_QUERY_CONTENT_FLAG_CERT |
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED) ,
CERT_QUERY_FORMAT_FLAG_ALL,
0,
NULL,
NULL,
NULL,
&hStoreMsg,
NULL,
NULL) )
goto ErrorCryptQueryObject;
hr = AddCertsToStores(hStoreMsg, plCertInstalled);
//don't treat cancel as error but return the err code
if (S_OK != hr && MY_HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
{
goto ErrorAddCertsToStores;
}
CommonReturn:
if(hStoreMsg != NULL)
CertCloseStore(hStoreMsg, 0);
if (NULL != pwszNotSafeAccepting)
LocalFree(pwszNotSafeAccepting);
if (NULL != pwszTitle)
LocalFree(pwszTitle);
// don't know if we have an error or not
// but I do know the errBefore is set properly
SetLastError(errBefore);
LeaveCriticalSection(&m_csXEnroll);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// We have an error, make sure we set it.
errBefore = GetLastError();
goto CommonReturn;
TRACE_ERROR(ErrorCryptQueryObject);
TRACE_ERROR(ErrorAddCertsToStores);
TRACE_ERROR(CancelledError);
TRACE_ERROR(xeLoadRCStringError);
}
HRESULT STDMETHODCALLTYPE CCEnroll::InstallPKCS7(
/* [in] */ BSTR wszPKCS7)
{
CRYPT_DATA_BLOB blobPKCS7;
assert(wszPKCS7 != NULL);
// just put into a blob
memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
blobPKCS7.pbData = (PBYTE) wszPKCS7;
// install the blob
return(InstallPKCS7Blob(&blobPKCS7));
}
HRESULT STDMETHODCALLTYPE CCEnroll::InstallPKCS7Ex(
/* [in] */ BSTR wszPKCS7,
/* [out] */ LONG __RPC_FAR *plCertInstalled)
{
CRYPT_DATA_BLOB blobPKCS7;
assert(wszPKCS7 != NULL);
// just put into a blob
memset(&blobPKCS7, 0, sizeof(CRYPT_DATA_BLOB));
blobPKCS7.cbData = SysStringByteLen(wszPKCS7);
blobPKCS7.pbData = (PBYTE) wszPKCS7;
// install the blob
return(InstallPKCS7BlobEx(&blobPKCS7, plCertInstalled));
}
// this is a scary routine. Put in for louis, use at your own risk.
HRESULT STDMETHODCALLTYPE CCEnroll::Reset(void)
{
HRESULT hr;
EnterCriticalSection(&m_csXEnroll);
Destruct();
hr = Init();
LeaveCriticalSection(&m_csXEnroll);
return hr;
}
HRESULT STDMETHODCALLTYPE CCEnroll::GetSupportedKeySpec(
LONG __RPC_FAR *pdwKeySpec) {
DWORD errBefore = GetLastError();
DWORD hr = S_OK;
DWORD cb = sizeof(DWORD);
SetLastError(ERROR_SUCCESS);
assert(pdwKeySpec != NULL);
*pdwKeySpec = 0;
EnterCriticalSection(&m_csXEnroll);
hr = GetVerifyProv();
if (S_OK != hr)
{
goto GetVerifyProvError;
}
if( !CryptGetProvParam(
m_hVerifyProv,
PP_KEYSPEC,
(BYTE *) pdwKeySpec,
&cb,
0
) )
goto ErrorCryptGetProvParam;
CommonReturn:
SetLastError(errBefore);
LeaveCriticalSection(&m_csXEnroll);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
// We have an error, make sure we set it.
errBefore = GetLastError();
goto CommonReturn;
TRACE_ERROR(ErrorCryptGetProvParam);
TRACE_ERROR(GetVerifyProvError);
}
HRESULT STDMETHODCALLTYPE CCEnroll::GetKeyLenEx(
LONG lSizeSpec,
LONG lKeySpec,
LONG __RPC_FAR *pdwKeySize)
{
BOOL fKeyX;
BOOL fKeyInc = FALSE;
DWORD dwKeySize = 0xFFFFFFFF;
DWORD cb;
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
switch (lKeySpec)
{
case XEKL_KEYSPEC_KEYX:
fKeyX = TRUE;
break;
case XEKL_KEYSPEC_SIG:
fKeyX = FALSE;
break;
default:
//invalid parameter
hr = E_INVALIDARG;
goto InvalidArgError;
}
switch (lSizeSpec)
{
case XEKL_KEYSIZE_MIN:
case XEKL_KEYSIZE_MAX:
case XEKL_KEYSIZE_DEFAULT:
break;
case XEKL_KEYSIZE_INC:
fKeyInc = TRUE;
break;
default:
//invalid parameter
hr = E_INVALIDARG;
goto InvalidArgError;
}
if (!fKeyInc)
{
DWORD dwAlg = (fKeyX ? ALG_CLASS_KEY_EXCHANGE : ALG_CLASS_SIGNATURE);
*pdwKeySize = GetKeySizeInfo(lSizeSpec, dwAlg);
if(0xFFFFFFFF == *pdwKeySize)
{
*pdwKeySize = 0;
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto GetKeySizeInfoError;
}
}
else
{
if ((fKeyX && (0 != m_dwXhgKeyLenInc)) ||
(!fKeyX && (0 != m_dwSigKeyLenInc)))
{
//we got the cached inc size
if (fKeyX)
{
*pdwKeySize = m_dwXhgKeyLenInc;
}
else
{
*pdwKeySize = m_dwSigKeyLenInc;
}
}
else
{
hr = GetVerifyProv();
if (S_OK != hr)
{
goto GetVerifyProvError;
}
//init
*pdwKeySize = 0;
cb = sizeof(dwKeySize);
if (!CryptGetProvParam(
m_hVerifyProv,
fKeyX ? PP_KEYX_KEYSIZE_INC : PP_SIG_KEYSIZE_INC,
(BYTE*)&dwKeySize,
&cb,
0))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptGetProvParamError;
}
else
{
*pdwKeySize = dwKeySize;
//cache it
if (fKeyX)
{
m_dwXhgKeyLenInc = dwKeySize;
}
else
{
m_dwSigKeyLenInc = dwKeySize;
}
}
}
}
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(CryptGetProvParamError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(GetKeySizeInfoError)
}
HRESULT STDMETHODCALLTYPE CCEnroll::GetKeyLen(
BOOL fMin,
BOOL fExchange,
LONG __RPC_FAR *pdwKeySize) {
DWORD hr = S_OK;
LONG lKeySizeSpec = (fMin ? XEKL_KEYSIZE_MIN : XEKL_KEYSIZE_MAX);
if(fExchange)
*pdwKeySize = GetKeySizeInfo(lKeySizeSpec, ALG_CLASS_KEY_EXCHANGE);
else
*pdwKeySize = GetKeySizeInfo(lKeySizeSpec, ALG_CLASS_SIGNATURE);
if(*pdwKeySize == 0xFFFFFFFF) {
*pdwKeySize = 0;
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
return(hr);
}
DWORD CCEnroll::GetKeySizeInfo(
LONG lKeySizeSpec,
DWORD algClass) {
DWORD cb = sizeof(PROV_ENUMALGS_EX);
HRESULT hr = S_OK;
DWORD errBefore = GetLastError();
DWORD dwFlags = CRYPT_FIRST;
PROV_ENUMALGS_EX algInfo;
DWORD dwKeySize = 0xFFFFFFFF;
DWORD err = ERROR_SUCCESS;
#ifdef DBG
//only accept two flags
assert(ALG_CLASS_KEY_EXCHANGE == algClass ||
ALG_CLASS_SIGNATURE == algClass);
#endif //DBG
SetLastError(ERROR_SUCCESS);
memset(&algInfo, 0, sizeof(algInfo));
EnterCriticalSection(&m_csXEnroll);
if ((ALG_CLASS_KEY_EXCHANGE == algClass && 0 != m_dwXhgKeyLenMax) ||
(ALG_CLASS_SIGNATURE == algClass && 0 != m_dwSigKeyLenMax))
{
//got cached sizes, use only KeyLenMax as check
#if DBG
if (ALG_CLASS_KEY_EXCHANGE == algClass)
{
assert(0 != m_dwXhgKeyLenMin);
assert(0 != m_dwXhgKeyLenDef);
}
if (ALG_CLASS_SIGNATURE == algClass)
{
assert(0 != m_dwSigKeyLenMin);
assert(0 != m_dwSigKeyLenDef);
}
#endif //DBG
//OK, cached, easy
}
else
{
#if DBG
if (ALG_CLASS_KEY_EXCHANGE == algClass)
{
assert(0 == m_dwXhgKeyLenMin);
assert(0 == m_dwXhgKeyLenDef);
}
if (ALG_CLASS_SIGNATURE == algClass)
{
assert(0 == m_dwSigKeyLenMin);
assert(0 == m_dwSigKeyLenDef);
}
#endif //DBG
hr = GetVerifyProv();
if (S_OK != hr)
{
goto GetVerifyProvError;
}
while (CryptGetProvParam(
m_hVerifyProv,
PP_ENUMALGS_EX,
(BYTE *) &algInfo,
&cb,
dwFlags))
{
// get rid of CRYPT_FIRST flag
dwFlags = 0;
if (ALG_CLASS_KEY_EXCHANGE == GET_ALG_CLASS(algInfo.aiAlgid))
{
//cache them
m_dwXhgKeyLenMax = algInfo.dwMaxLen;
m_dwXhgKeyLenMin = algInfo.dwMinLen;
m_dwXhgKeyLenDef = algInfo.dwDefaultLen;
}
else if (ALG_CLASS_SIGNATURE == GET_ALG_CLASS(algInfo.aiAlgid))
{
m_dwSigKeyLenMax = algInfo.dwMaxLen;
m_dwSigKeyLenMin = algInfo.dwMinLen;
m_dwSigKeyLenDef = algInfo.dwDefaultLen;
}
//see if we cache all sizes through single enum loop
if (0 != m_dwXhgKeyLenMax &&
0 != m_dwXhgKeyLenMin &&
0 != m_dwXhgKeyLenDef &&
0 != m_dwSigKeyLenMax &&
0 != m_dwSigKeyLenMin &&
0 != m_dwSigKeyLenDef)
{
//looks we cached all
break;
}
}
}
// if we got here,
// either PP_ENUMALGS_EX is not supported by CSP , should return error
// or csp doesn't support specified algorithm, should ERROR_NO_MORE_ITEMS
err = GetLastError();
if (err != ERROR_SUCCESS)
{
if (err != ERROR_NO_MORE_ITEMS)
{
goto ErrorCryptGetProvParam;
}
// should be ERROR_NO_MORE_ITEMS
if ((ALG_CLASS_KEY_EXCHANGE == algClass && 0 != m_dwXhgKeyLenMax) ||
(ALG_CLASS_SIGNATURE == algClass && 0 != m_dwSigKeyLenMax))
{
//we may get here because the csp is signature or exchange only
//so we cannot cache both once
SetLastError(ERROR_SUCCESS);
}
else
{
SetLastError((DWORD)NTE_BAD_ALGID);
}
}
//should have all sizes
if(XEKL_KEYSIZE_MIN == lKeySizeSpec)
{
if (ALG_CLASS_KEY_EXCHANGE == algClass)
{
dwKeySize = m_dwXhgKeyLenMin;
}
else
{
dwKeySize = m_dwSigKeyLenMin;
}
}
else if (XEKL_KEYSIZE_MAX == lKeySizeSpec)
{
if (ALG_CLASS_KEY_EXCHANGE == algClass)
{
dwKeySize = m_dwXhgKeyLenMax;
}
else
{
dwKeySize = m_dwSigKeyLenMax;
}
}
else if (XEKL_KEYSIZE_DEFAULT == lKeySizeSpec)
{
if (ALG_CLASS_KEY_EXCHANGE == algClass)
{
dwKeySize = m_dwXhgKeyLenDef;
}
else
{
dwKeySize = m_dwSigKeyLenDef;
}
}
CommonReturn:
SetLastError(errBefore);
LeaveCriticalSection(&m_csXEnroll);
return(dwKeySize);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
// We have an error, make sure we set it.
errBefore = GetLastError();
goto CommonReturn;
TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
}
HRESULT STDMETHODCALLTYPE CCEnroll::EnumAlgs(
/* [in] */ LONG dwIndex,
/* [in] */ LONG algMask,
/* [out] */ LONG __RPC_FAR *pdwAlgID) {
DWORD errBefore = GetLastError();
PROV_ENUMALGS enumAlgs;
DWORD cb = sizeof(enumAlgs);
LONG i = 0;
HRESULT hr = S_OK;
DWORD dwFlags;
BOOL f1st = TRUE;
SetLastError(ERROR_SUCCESS);
memset(&enumAlgs, 0, sizeof(enumAlgs));
assert(pdwAlgID != NULL);
*pdwAlgID = 0;
EnterCriticalSection(&m_csXEnroll);
hr = GetVerifyProv();
if (S_OK != hr)
{
goto GetVerifyProvError;
}
if (MAXDWORD != m_dwLastAlgIndex &&
((DWORD)dwIndex) == m_dwLastAlgIndex + 1)
{
//continue enum
dwFlags = 0;
while (f1st || (DWORD)algMask != GET_ALG_CLASS(enumAlgs.aiAlgid))
{
if(!CryptGetProvParam(
m_hVerifyProv,
PP_ENUMALGS,
(BYTE*)&enumAlgs,
&cb,
dwFlags))
{
goto ErrorCryptGetProvParam;
}
f1st = FALSE;
}
}
else
{
dwFlags = CRYPT_FIRST;
for (i = 0; i <= dwIndex; i++)
{
if(!CryptGetProvParam(
m_hVerifyProv,
PP_ENUMALGS,
(BYTE*)&enumAlgs,
&cb,
dwFlags))
{
goto ErrorCryptGetProvParam;
}
dwFlags = 0;
// if we have not hit something we are counting, do it again
if ((DWORD)algMask != GET_ALG_CLASS(enumAlgs.aiAlgid))
{
i--;
}
}
}
//update cached index
m_dwLastAlgIndex = dwIndex;
*pdwAlgID = enumAlgs.aiAlgid;
CommonReturn:
SetLastError(errBefore);
LeaveCriticalSection(&m_csXEnroll);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// We have an error, make sure we set it.
errBefore = GetLastError();
//error, reset index
m_dwLastAlgIndex = MAXDWORD;
goto CommonReturn;
TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
}
HRESULT STDMETHODCALLTYPE CCEnroll::GetAlgNameWStr(
/* [in] */ LONG algID,
/* [out] */ LPWSTR __RPC_FAR *ppwsz) {
DWORD errBefore = GetLastError();
PROV_ENUMALGS enumAlgs;
DWORD cb = sizeof(enumAlgs);
HRESULT hr = S_OK;
DWORD dwFlags = CRYPT_FIRST;
SetLastError(ERROR_SUCCESS);
memset(&enumAlgs, 0, sizeof(enumAlgs));
EnterCriticalSection(&m_csXEnroll);
hr = GetVerifyProv();
if (S_OK != hr)
{
goto GetVerifyProvError;
}
do {
if( !CryptGetProvParam(
m_hVerifyProv,
PP_ENUMALGS,
(BYTE *) &enumAlgs,
&cb,
dwFlags) )
goto ErrorCryptGetProvParam;
dwFlags = 0;
} while((DWORD)algID != enumAlgs.aiAlgid);
if( (*ppwsz = WideFromMB(enumAlgs.szName)) == NULL )
goto ErrorOutOfMem;
CommonReturn:
SetLastError(errBefore);
LeaveCriticalSection(&m_csXEnroll);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// We have an error, make sure we set it.
errBefore = GetLastError();
goto CommonReturn;
TRACE_ERROR(GetVerifyProvError);
TRACE_ERROR(ErrorCryptGetProvParam);
TRACE_ERROR(ErrorOutOfMem);
}
HRESULT STDMETHODCALLTYPE CCEnroll::GetAlgName(
/* [in] */ LONG algID,
/* [out][retval] */ BSTR __RPC_FAR *pbstr) {
DWORD errBefore = GetLastError();
LPWSTR pwsz = NULL;
HRESULT hr = S_OK;
SetLastError(ERROR_SUCCESS);
assert(pbstr != NULL);
if((hr = GetAlgNameWStr(algID, &pwsz)) != S_OK)
goto ErrorgetAlgNameWStr;
if( (*pbstr = SysAllocString(pwsz)) == NULL )
goto ErrorSysAllocString;
CommonReturn:
if(pwsz != NULL)
MyCoTaskMemFree(pwsz);
SetLastError(errBefore);
return(hr);
ErrorReturn:
if(GetLastError() == ERROR_SUCCESS)
SetLastError((DWORD)E_UNEXPECTED);
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// We have an error, make sure we set it.
errBefore = GetLastError();
goto CommonReturn;
TRACE_ERROR(ErrorgetAlgNameWStr);
TRACE_ERROR(ErrorSysAllocString);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_ReuseHardwareKeyIfUnableToGenNew(
/* [retval][out] */ BOOL __RPC_FAR *fBool) {
EnterCriticalSection(&m_csXEnroll);
*fBool = m_fReuseHardwareKeyIfUnableToGenNew;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_ReuseHardwareKeyIfUnableToGenNew(
/* [in] */ BOOL fBool) {
EnterCriticalSection(&m_csXEnroll);
m_fReuseHardwareKeyIfUnableToGenNew = fBool;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreMy(
HCERTSTORE hStore
) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_MyStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_MyStore.wszName != wszMY)
MyCoTaskMemFree(m_MyStore.wszName);
m_MyStore.wszName = NULL;
m_MyStore.hStore = CertDuplicateStore(hStore);
}
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreCA(
HCERTSTORE hStore
) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_CAStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_CAStore.wszName != wszCA)
MyCoTaskMemFree(m_CAStore.wszName);
m_CAStore.wszName = NULL;
m_CAStore.hStore = CertDuplicateStore(hStore);
}
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreROOT(
HCERTSTORE hStore
) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_RootStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_RootStore.wszName != wszROOT && m_RootStore.wszName != wszCA)
MyCoTaskMemFree(m_RootStore.wszName);
m_RootStore.wszName = NULL;
m_RootStore.hStore = CertDuplicateStore(hStore);
}
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::SetHStoreRequest(
HCERTSTORE hStore
) {
HRESULT hr = S_OK;
EnterCriticalSection(&m_csXEnroll);
if(m_RequestStore.hStore != NULL)
hr = E_ACCESSDENIED;
else {
if(m_RequestStore.wszName != wszREQUEST)
MyCoTaskMemFree(m_RequestStore.wszName);
m_RequestStore.wszName = NULL;
m_RequestStore.hStore = CertDuplicateStore(hStore);
}
LeaveCriticalSection(&m_csXEnroll);
return(hr);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_LimitExchangeKeyToEncipherment(
BOOL fBool
) {
EnterCriticalSection(&m_csXEnroll);
m_fLimitExchangeKeyToEncipherment = fBool;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_LimitExchangeKeyToEncipherment(
BOOL * fBool
) {
EnterCriticalSection(&m_csXEnroll);
*fBool = m_fLimitExchangeKeyToEncipherment;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
HRESULT STDMETHODCALLTYPE CCEnroll::put_EnableSMIMECapabilities(
BOOL fSMIME
)
{
HRESULT hr;
EnterCriticalSection(&m_csXEnroll);
if (m_fKeySpecSetByClient)
{
//SMIME is set by the client
if (AT_SIGNATURE == m_keyProvInfo.dwKeySpec && fSMIME)
{
//try to set signature key spec also SMIME
hr = XENROLL_E_KEYSPEC_SMIME_MISMATCH;
goto MismatchError;
}
}
else
{
//user didn't set key spec
//determine the spec accordingly
m_keyProvInfo.dwKeySpec = fSMIME ? AT_KEYEXCHANGE : AT_SIGNATURE;
}
m_fEnableSMIMECapabilities = fSMIME;
m_fSMIMESetByClient = TRUE;
hr = S_OK;
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return(hr);
TRACE_ERROR(MismatchError)
}
HRESULT STDMETHODCALLTYPE CCEnroll::get_EnableSMIMECapabilities(
BOOL * fBool
) {
EnterCriticalSection(&m_csXEnroll);
*fBool = m_fEnableSMIMECapabilities;
LeaveCriticalSection(&m_csXEnroll);
return(S_OK);
}
//ICEnroll4
HRESULT
GetCertificateContextFromBStr(
IN BSTR bstrCert,
OUT PCCERT_CONTEXT *ppCert)
{
HRESULT hr;
PCCERT_CONTEXT pCert = NULL;
BYTE *pbCert = NULL;
DWORD cbCert = 0;
// could be any form, binary or base64
while (TRUE)
{
if (!MyCryptStringToBinaryW(
(WCHAR*)bstrCert,
SysStringLen(bstrCert),
CRYPT_STRING_ANY,
pbCert,
&cbCert,
NULL,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto MyCryptStringToBinaryWError;
}
if (NULL != pbCert)
{
break; //done
}
pbCert = (BYTE*)LocalAlloc(LMEM_FIXED, cbCert);
if (NULL == pbCert)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
pCert = CertCreateCertificateContext(
X509_ASN_ENCODING,
pbCert,
cbCert);
if (NULL == pCert)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertCreateCertificateContextError;
}
*ppCert = pCert;
pCert = NULL;
hr = S_OK;
ErrorReturn:
if (NULL != pbCert)
{
LocalFree(pbCert);
}
if (NULL != pCert)
{
CertFreeCertificateContext(pCert);
}
return (hr);
TRACE_ERROR(CertCreateCertificateContextError)
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(OutOfMemoryError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::put_PrivateKeyArchiveCertificate(
IN BSTR bstrCert)
{
HRESULT hr;
PCCERT_CONTEXT pPrivateKeyArchiveCert = NULL;
if (NULL != bstrCert)
{
hr = GetCertificateContextFromBStr(bstrCert, &pPrivateKeyArchiveCert);
if (S_OK != hr)
{
goto GetCertificateContextFromBStrError;
}
}
// set key archive certificate
hr = SetPrivateKeyArchiveCertificate(pPrivateKeyArchiveCert);
if (S_OK != hr)
{
goto SetPrivateKeyArchiveCertificateError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pPrivateKeyArchiveCert)
{
CertFreeCertificateContext(pPrivateKeyArchiveCert);
}
return hr;
TRACE_ERROR(GetCertificateContextFromBStrError)
TRACE_ERROR(SetPrivateKeyArchiveCertificateError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::get_PrivateKeyArchiveCertificate(
OUT BSTR __RPC_FAR *pbstrCert)
{
HRESULT hr;
PCCERT_CONTEXT pPrivateKeyArchiveCert = NULL;
CRYPT_DATA_BLOB blobCert;
//init
*pbstrCert = NULL;
pPrivateKeyArchiveCert = GetPrivateKeyArchiveCertificate();
if (NULL != pPrivateKeyArchiveCert)
{
blobCert.pbData = pPrivateKeyArchiveCert->pbCertEncoded;
blobCert.cbData = pPrivateKeyArchiveCert->cbCertEncoded;
hr = BlobToBstring(&blobCert, CRYPT_STRING_BASE64HEADER, pbstrCert);
if (S_OK != hr)
{
goto BlobToBstringError;
}
}
hr = S_OK;
ErrorReturn:
if (NULL != pPrivateKeyArchiveCert)
{
CertFreeCertificateContext(pPrivateKeyArchiveCert);
}
return hr;
TRACE_ERROR(BlobToBstringError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::put_ThumbPrint(IN BSTR bstrThumbPrint)
{
CRYPT_DATA_BLOB hashBlob;
HRESULT hr;
if (bstrThumbPrint == NULL)
return E_INVALIDARG;
hashBlob.cbData = 0;
hashBlob.pbData = NULL;
if (!MyCryptStringToBinaryW
((WCHAR*)bstrThumbPrint,
SysStringLen(bstrThumbPrint),
CRYPT_STRING_BASE64,
hashBlob.pbData,
&hashBlob.cbData,
NULL,
NULL))
goto MyCryptToBinaryErr;
hashBlob.pbData = (LPBYTE)LocalAlloc(LPTR, hashBlob.cbData);
if (NULL == hashBlob.pbData)
goto MemoryErr;
if (!MyCryptStringToBinaryW
((WCHAR*)bstrThumbPrint,
SysStringLen(bstrThumbPrint),
CRYPT_STRING_BASE64,
hashBlob.pbData,
&hashBlob.cbData,
NULL,
NULL))
goto MyCryptToBinaryErr;
hr = this->put_ThumbPrintWStr(hashBlob);
ErrorReturn:
if (NULL != hashBlob.pbData) { LocalFree(hashBlob.pbData); }
return hr;
SET_HRESULT(MyCryptToBinaryErr, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(MemoryErr, E_OUTOFMEMORY);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::put_ThumbPrintWStr(IN CRYPT_DATA_BLOB hashBlob)
{
if (hashBlob.pbData == NULL)
return E_INVALIDARG;
if (m_hashBlobPendingRequest.pbData != NULL)
{
LocalFree(m_hashBlobPendingRequest.pbData);
m_hashBlobPendingRequest.pbData = NULL;
}
m_hashBlobPendingRequest.cbData = hashBlob.cbData;
m_hashBlobPendingRequest.pbData = (LPBYTE)LocalAlloc(LPTR, m_hashBlobPendingRequest.cbData);
if (m_hashBlobPendingRequest.pbData == NULL)
return E_OUTOFMEMORY;
CopyMemory(m_hashBlobPendingRequest.pbData, hashBlob.pbData, hashBlob.cbData);
return S_OK;
}
HRESULT STDMETHODCALLTYPE
CCEnroll::get_ThumbPrint(OUT BSTR __RPC_FAR *pbstrThumbPrint)
{
CRYPT_DATA_BLOB hashBlob;
DWORD cchThumbPrintStr;
HRESULT hr;
WCHAR *pwszThumbPrint = NULL;
int i, n;
// Input validation:
if (pbstrThumbPrint == NULL)
return E_INVALIDARG;
// Initialize locals:
ZeroMemory(&hashBlob, sizeof(hashBlob));
*pbstrThumbPrint = NULL;
if (S_OK != (hr = this->get_ThumbPrintWStr(&hashBlob)))
goto ErrorReturn;
hashBlob.pbData = (LPBYTE)LocalAlloc(LPTR, hashBlob.cbData);
if (NULL == hashBlob.pbData)
goto MemoryErr;
if (S_OK != (hr = this->get_ThumbPrintWStr(&hashBlob)))
goto ErrorReturn;
// Now we have a binary thumbprint. Convert this to base64:
while (TRUE)
{
if (!MyCryptBinaryToStringW(
hashBlob.pbData,
hashBlob.cbData,
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCR,
pwszThumbPrint,
&cchThumbPrintStr))
{
goto MyCryptToStringErr;
}
if (NULL != pwszThumbPrint)
{
//done
break;
}
pwszThumbPrint = (WCHAR*)LocalAlloc(LMEM_FIXED,
cchThumbPrintStr * sizeof(WCHAR));
if (NULL == pwszThumbPrint)
{
goto MemoryErr;
}
}
//make sure no new line and CR
n = (int)wcslen(pwszThumbPrint);
for (i = n - 1; i > -1; --i)
{
if (L'\r' != pwszThumbPrint[i] &&
L'\n' != pwszThumbPrint[i])
{
break; //done
}
pwszThumbPrint[i] = L'\0'; //null it
}
// Ok, we've acquired the HASH. Now copy it to the out parameter:
*pbstrThumbPrint = SysAllocString(pwszThumbPrint);
if (NULL == *pbstrThumbPrint)
{
goto MemoryErr;
}
hr = S_OK;
ErrorReturn:
if (NULL != hashBlob.pbData)
{
LocalFree(hashBlob.pbData);
}
if (NULL != pwszThumbPrint)
{
LocalFree(pwszThumbPrint);
}
return hr;
SET_HRESULT(MyCryptToStringErr, MY_HRESULT_FROM_WIN32(GetLastError()))
SET_HRESULT(MemoryErr, E_OUTOFMEMORY)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::get_ThumbPrintWStr(IN OUT PCRYPT_DATA_BLOB pHashBlob) {
HRESULT hr = S_OK;
// Input validation:
if (NULL == pHashBlob)
return E_INVALIDARG;
// TWO CASES:
//
// 1) the thumbprint has been explicitly set by an external caller.
// 2) the thumbprint _wasn't_ explicitly set. In this case, use the thumbprint
// of the request generated by the last call to createPKCS10().
//
// CASE 1:
//
if (NULL != m_hashBlobPendingRequest.pbData)
{
if (NULL != pHashBlob->pbData)
{
if (pHashBlob->cbData < m_hashBlobPendingRequest.cbData) {
hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
else {
CopyMemory(pHashBlob->pbData, m_hashBlobPendingRequest.pbData, m_hashBlobPendingRequest.cbData);
hr = S_OK;
}
}
pHashBlob->cbData = m_hashBlobPendingRequest.cbData;
return hr;
}
// CASE 2:
//
else
{
if (NULL == m_pCertContextPendingRequest)
return E_POINTER;
// Executes at most twice.
if (!CertGetCertificateContextProperty
(m_pCertContextPendingRequest,
CERT_HASH_PROP_ID,
(LPVOID)(pHashBlob->pbData),
&(pHashBlob->cbData)))
{
return MY_HRESULT_FROM_WIN32(GetLastError());
}
return S_OK;
}
}
HRESULT STDMETHODCALLTYPE
CCEnroll::binaryToString(
IN LONG Flags,
IN BSTR strBinary,
OUT BSTR *pstrEncoded)
{
HRESULT hr;
CRYPT_DATA_BLOB blobBinary;
WCHAR *pwszEncoded = NULL;
blobBinary.pbData = (BYTE*)strBinary;
blobBinary.cbData = SysStringByteLen(strBinary);
hr = binaryBlobToString(Flags, &blobBinary, &pwszEncoded);
if (S_OK != hr)
{
goto binaryBlobToStringError;
}
*pstrEncoded = SysAllocString(pwszEncoded);
if (NULL == pstrEncoded)
{
hr = E_OUTOFMEMORY;
goto SysAllocStringLenError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pwszEncoded)
{
MyCoTaskMemFree(pwszEncoded);
}
return hr;
TRACE_ERROR(binaryBlobToStringError);
TRACE_ERROR(SysAllocStringLenError);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::stringToBinary(
IN LONG Flags,
IN BSTR strEncoded,
OUT BSTR *pstrBinary)
{
HRESULT hr;
CRYPT_DATA_BLOB blobBinary;
ZeroMemory(&blobBinary, sizeof(blobBinary));
hr = stringToBinaryBlob(Flags, (LPCWSTR)strEncoded, &blobBinary, NULL, NULL);
if (S_OK != hr)
{
goto stringToBinaryBlobError;
}
*pstrBinary = SysAllocStringLen(
(OLECHAR*)blobBinary.pbData, blobBinary.cbData);
if (NULL == *pstrBinary)
{
hr = E_OUTOFMEMORY;
goto SysAllocStringLenError;
}
hr = S_OK;
ErrorReturn:
if (NULL != blobBinary.pbData)
{
MyCoTaskMemFree(blobBinary.pbData);
}
return hr;
TRACE_ERROR(stringToBinaryBlobError);
TRACE_ERROR(SysAllocStringLenError);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::addExtensionToRequest(
IN LONG Flags,
IN BSTR strName,
IN BSTR strValue)
{
HRESULT hr;
CRYPT_DATA_BLOB blobValue;
DWORD cchStrValue = SysStringLen(strValue);
BYTE *pbExtVal = NULL;
DWORD cbExtVal = 0;
//convert to binary in case base64 etc.
while (TRUE)
{
if (!MyCryptStringToBinaryW(
(WCHAR*)strValue,
cchStrValue,
CRYPT_STRING_ANY,
pbExtVal,
&cbExtVal,
NULL,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto MyCryptStringToBinaryWError;
}
if (NULL != pbExtVal)
{
//done
break;
}
pbExtVal = (BYTE*)LocalAlloc(LMEM_FIXED, cbExtVal);
if (NULL == pbExtVal)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
}
blobValue.pbData = pbExtVal;
blobValue.cbData = cbExtVal;
hr = addExtensionToRequestWStr(Flags, strName, &blobValue);
if (S_OK != hr)
{
goto addExtensionToRequestWStrError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pbExtVal)
{
LocalFree(pbExtVal);
}
return hr;
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(addExtensionToRequestWStrError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::addAttributeToRequest(
IN LONG Flags,
IN BSTR strName,
IN BSTR strValue)
{
HRESULT hr;
CRYPT_DATA_BLOB blobValue;
DWORD cchStrValue = SysStringLen(strValue);
BYTE *pbAttVal = NULL;
DWORD cbAttVal = 0;
//convert to binary in case base64 etc.
while (TRUE)
{
if (!MyCryptStringToBinaryW(
(WCHAR*)strValue,
cchStrValue,
CRYPT_STRING_ANY,
pbAttVal,
&cbAttVal,
NULL,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto MyCryptStringToBinaryWError;
}
if (NULL != pbAttVal)
{
//done
break;
}
pbAttVal = (BYTE*)LocalAlloc(LMEM_FIXED, cbAttVal);
if (NULL == pbAttVal)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
}
blobValue.pbData = pbAttVal;
blobValue.cbData = cbAttVal;
hr = addAttributeToRequestWStr(Flags, strName, &blobValue);
if (S_OK != hr)
{
goto addAttributeToRequestWStrError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pbAttVal)
{
LocalFree(pbAttVal);
}
return hr;
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(LocalAllocError)
TRACE_ERROR(addAttributeToRequestWStrError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::addNameValuePairToRequest(
IN LONG Flags, //not used
IN BSTR strName,
IN BSTR strValue)
{
return addNameValuePairToRequestWStr(Flags, strName, strValue);
}
HRESULT STDMETHODCALLTYPE CCEnroll::addBlobPropertyToCertificate(
IN LONG lPropertyId,
IN LONG lFlags,
IN BSTR strProperty)
{
CRYPT_DATA_BLOB blob;
blob.pbData = (BYTE*)strProperty;
blob.cbData = SysStringByteLen(strProperty);
if (0x0 != (XECP_STRING_PROPERTY & lFlags))
{
//this is a string property, including null
blob.cbData += sizeof(WCHAR);
}
return addBlobPropertyToCertificateWStr(lPropertyId, lFlags, &blob);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::put_SignerCertificate(
IN BSTR bstrCert)
{
HRESULT hr;
PCCERT_CONTEXT pSignerCert = NULL;
if (NULL != bstrCert)
{
hr = GetCertificateContextFromBStr(bstrCert, &pSignerCert);
if (S_OK != hr)
{
goto GetCertificateContextFromBStrError;
}
}
// set key archive certificate
hr = SetSignerCertificate(pSignerCert);
if (S_OK != hr)
{
goto SetSignerCertificateError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pSignerCert)
{
CertFreeCertificateContext(pSignerCert);
}
return hr;
TRACE_ERROR(GetCertificateContextFromBStrError)
TRACE_ERROR(SetSignerCertificateError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::resetExtensions()
{
HRESULT hr = S_OK;
FreeAllStackExtension();
return hr;
}
HRESULT STDMETHODCALLTYPE
CCEnroll::resetAttributes()
{
HRESULT hr = S_OK;
FreeAllStackAttribute();
return hr;
}
HRESULT STDMETHODCALLTYPE
CCEnroll::createRequest(
IN LONG Flags,
IN BSTR strDNName,
IN BSTR strUsage,
OUT BSTR *pstrRequest)
{
return createRequestWStrBStr(
Flags,
(LPCWSTR)strDNName,
(LPCWSTR)strUsage,
CRYPT_STRING_BASE64REQUESTHEADER,
pstrRequest);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::createFileRequest(
IN LONG Flags,
IN BSTR strDNName,
IN BSTR strUsage,
IN BSTR strRequestFileName)
{
return createFileRequestWStr(Flags, (LPCWSTR)strDNName, (LPCWSTR)strUsage, (LPCWSTR)strRequestFileName);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::acceptResponse(
IN BSTR bstrResponse)
{
HRESULT hr;
CRYPT_DATA_BLOB blobResponse;
DWORD cchStrResponse;
ZeroMemory(&blobResponse, sizeof(blobResponse));
if (NULL == bstrResponse)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto InvalidParameterError;
}
//assume a string
cchStrResponse = SysStringLen(bstrResponse);
//convert to binary in case base64 etc.
while (TRUE)
{
if (!MyCryptStringToBinaryW(
(WCHAR*)bstrResponse,
cchStrResponse,
CRYPT_STRING_ANY,
blobResponse.pbData,
&blobResponse.cbData,
NULL,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto MyCryptStringToBinaryWError;
}
if (NULL != blobResponse.pbData)
{
//done
break;
}
blobResponse.pbData = (BYTE*)LocalAlloc(
LMEM_FIXED, blobResponse.cbData);
if (NULL == blobResponse.pbData)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
}
// accept the blob
hr = acceptResponseBlob(&blobResponse);
if (S_OK != hr)
{
goto acceptResponseBlobError;
}
hr = S_OK;
ErrorReturn:
if (NULL != blobResponse.pbData)
{
LocalFree(blobResponse.pbData);
}
return (hr);
TRACE_ERROR(acceptResponseBlobError)
TRACE_ERROR(InvalidParameterError)
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(LocalAllocError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::acceptFileResponse(
IN BSTR bstrResponseFileName)
{
return acceptFileResponseWStr((LPCWSTR)bstrResponseFileName);
}
HRESULT
CCEnroll::GetCertFromResponseBlobToBStr(
IN CRYPT_DATA_BLOB *pBlobResponse,
OUT BSTR *pstrCert)
{
HRESULT hr;
CRYPT_DATA_BLOB blobCert;
PCCERT_CONTEXT pCert = NULL;
hr = getCertContextFromResponseBlob(
pBlobResponse,
&pCert);
if (S_OK != hr)
{
goto getCertContextFromResponseBlobError;
}
assert(NULL != pCert);
blobCert.pbData = pCert->pbCertEncoded;
blobCert.cbData = pCert->cbCertEncoded;
hr = BlobToBstring(&blobCert, CRYPT_STRING_BASE64HEADER, pstrCert);
if (S_OK != hr)
{
goto BlobToBstringError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pCert)
{
CertFreeCertificateContext(pCert);
}
return hr;
TRACE_ERROR(getCertContextFromResponseBlobError)
TRACE_ERROR(BlobToBstringError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::getCertFromResponse(
IN BSTR strResponse,
OUT BSTR *pstrCert)
{
HRESULT hr;
CRYPT_DATA_BLOB blobResponse;
ZeroMemory(&blobResponse, sizeof(blobResponse));
if (NULL == strResponse)
{
hr = E_POINTER;
goto NullPointerError;
}
hr = BstringToBlob(strResponse, &blobResponse);
if (S_OK != hr)
{
goto BstringToBlobError;
}
hr = GetCertFromResponseBlobToBStr(
&blobResponse,
pstrCert);
if (S_OK != hr)
{
goto GetCertFromResponseBlobToBStrError;
}
hr = S_OK;
ErrorReturn:
return hr;
TRACE_ERROR(NullPointerError)
TRACE_ERROR(BstringToBlobError)
TRACE_ERROR(GetCertFromResponseBlobToBStrError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::getCertFromFileResponse(
IN BSTR strResponseFileName,
OUT BSTR *pstrCert)
{
HRESULT hr;
CRYPT_DATA_BLOB blobResponse;
ZeroMemory(&blobResponse, sizeof(blobResponse));
hr = xeStringToBinaryFromFile(
(LPCWSTR)strResponseFileName,
&blobResponse.pbData,
&blobResponse.cbData,
CRYPT_STRING_ANY);
if (S_OK != hr)
{
goto xeStringToBinaryFromFileError;
}
hr = GetCertFromResponseBlobToBStr(
&blobResponse,
pstrCert);
if (S_OK != hr)
{
goto GetCertFromResponseBlobToBStrError;
}
hr = S_OK;
ErrorReturn:
if (NULL != blobResponse.pbData)
{
LocalFree(blobResponse.pbData);
}
return hr;
TRACE_ERROR(xeStringToBinaryFromFileError)
TRACE_ERROR(GetCertFromResponseBlobToBStrError)
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CCEnroll::createPFX(
IN BSTR strPassword,
OUT BSTR *pstrPFX)
{
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
return E_ACCESSDENIED;
return createPFXWStrBStr((LPCWSTR)strPassword, pstrPFX);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::createFilePFX(
IN BSTR strPassword,
IN BSTR strPFXFileName)
{
return createFilePFXWStr((LPCWSTR)strPassword, (LPCWSTR)strPFXFileName);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::setPendingRequestInfo(
IN LONG lRequestID,
IN BSTR strCADNS,
IN BSTR strCAName,
IN BSTR strFriendlyName
)
{
return setPendingRequestInfoWStr(
lRequestID,
(LPCWSTR)strCADNS,
(LPCWSTR)strCAName,
(LPCWSTR)strFriendlyName);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::enumPendingRequest(
IN LONG lIndex,
IN LONG lDesiredProperty,
OUT VARIANT *pvarProperty
)
{
CRYPT_DATA_BLOB dataBlobProperty;
HRESULT hr;
LONG lProperty;
VARIANT varProperty;
// See if we're initializing an enumeration. If so, just dispatch to
// enumPendingRequestWStr:
if (XEPR_ENUM_FIRST == lIndex) {
return enumPendingRequestWStr(XEPR_ENUM_FIRST, 0, NULL);
}
// Input validation:
if (lIndex < 0 || NULL == pvarProperty)
return E_INVALIDARG;
// Initialize locals:
memset(&varProperty, 0, sizeof(VARIANT));
memset(&dataBlobProperty, 0, sizeof(CRYPT_DATA_BLOB));
switch (lDesiredProperty)
{
case XEPR_REQUESTID:
case XEPR_VERSION:
if (S_OK != (hr = enumPendingRequestWStr(lIndex, lDesiredProperty, &lProperty)))
goto ErrorReturn;
varProperty.vt = VT_I4;
varProperty.lVal = lProperty;
*pvarProperty = varProperty;
goto CommonReturn;
case XEPR_CANAME:
case XEPR_CAFRIENDLYNAME:
case XEPR_CADNS:
case XEPR_HASH:
case XEPR_V1TEMPLATENAME:
case XEPR_V2TEMPLATEOID:
dataBlobProperty.cbData = 0;
dataBlobProperty.pbData = NULL;
// Determine the size of the property we desire.
hr = enumPendingRequestWStr(lIndex, lDesiredProperty, (LPVOID)&dataBlobProperty);
if (S_OK != hr || 0 == dataBlobProperty.cbData)
goto ErrorReturn;
dataBlobProperty.pbData = (LPBYTE)LocalAlloc(LPTR, dataBlobProperty.cbData);
if (NULL == dataBlobProperty.pbData)
goto MemoryErr;
// Request the property, using our newly allocated buffer.
hr = enumPendingRequestWStr(lIndex, lDesiredProperty, (LPVOID)&dataBlobProperty);
if (hr != S_OK)
goto ErrorReturn;
varProperty.vt = VT_BSTR;
varProperty.bstrVal = SysAllocStringByteLen((LPCSTR)dataBlobProperty.pbData, dataBlobProperty.cbData);
if (NULL == varProperty.bstrVal)
goto MemoryErr;
*pvarProperty = varProperty;
goto CommonReturn;
case XEPR_DATE:
goto NotImplErr;
default:
goto InvalidArgErr;
}
CommonReturn:
if (NULL != dataBlobProperty.pbData) { LocalFree(dataBlobProperty.pbData); }
return hr;
ErrorReturn:
if (NULL != varProperty.bstrVal) { SysFreeString(varProperty.bstrVal); }
goto CommonReturn;
SET_HRESULT(InvalidArgErr, E_INVALIDARG);
SET_HRESULT(MemoryErr, E_OUTOFMEMORY);
SET_HRESULT(NotImplErr, E_NOTIMPL);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::removePendingRequest(
IN BSTR bstrThumbPrint
)
{
CRYPT_DATA_BLOB hashBlob;
HRESULT hr;
if (bstrThumbPrint == NULL)
return E_INVALIDARG;
hashBlob.cbData = 0;
hashBlob.pbData = NULL;
if (!MyCryptStringToBinaryW
((WCHAR*)bstrThumbPrint,
SysStringLen(bstrThumbPrint),
CRYPT_STRING_ANY,
hashBlob.pbData,
&hashBlob.cbData,
NULL,
NULL))
goto MyCryptToBinaryErr;
hashBlob.pbData = (LPBYTE)LocalAlloc(LPTR, hashBlob.cbData);
if (NULL == hashBlob.pbData)
goto MemoryErr;
if (!MyCryptStringToBinaryW
((WCHAR*)bstrThumbPrint,
SysStringLen(bstrThumbPrint),
CRYPT_STRING_ANY,
hashBlob.pbData,
&hashBlob.cbData,
NULL,
NULL))
goto MyCryptToBinaryErr;
hr = this->removePendingRequestWStr(hashBlob);
CommonReturn:
if (NULL != hashBlob.pbData) { LocalFree(hashBlob.pbData); }
return hr;
ErrorReturn:
goto CommonReturn;
SET_HRESULT(MyCryptToBinaryErr, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(MemoryErr, E_OUTOFMEMORY);
}
//IEnroll4
HRESULT
myCertGetNameString(
IN PCCERT_CONTEXT pCert,
IN BOOL fIssuer,
OUT WCHAR **ppwszName)
{
HRESULT hr;
DWORD dwFlags = fIssuer ? CERT_NAME_ISSUER_FLAG : 0;
DWORD dwTypePara;
WCHAR *pwszName = NULL;
DWORD cch = 0;
while (TRUE)
{
cch = CertGetNameStringW(
pCert,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
dwFlags,
(void*)&dwTypePara,
pwszName,
cch);
if (0 == cch)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertGetNameStringError;
}
if (NULL != pwszName)
{
//done
break;
}
pwszName = (WCHAR*)LocalAlloc(LMEM_FIXED, cch * sizeof(WCHAR));
if (NULL == pwszName)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
*ppwszName = pwszName;
pwszName = NULL;
hr = S_OK;
ErrorReturn:
if (NULL != pwszName)
{
LocalFree(pwszName);
}
return hr;
TRACE_ERROR(CertGetNameStringError)
TRACE_ERROR(OutOfMemoryError)
}
HRESULT CCEnroll::GetGoodCertContext(
IN PCCERT_CONTEXT pCertContext,
OUT PCCERT_CONTEXT *ppGoodCertContext)
{
HRESULT hr;
PCCERT_CONTEXT pGoodCertContext = NULL;
DWORD cb;
//init
*ppGoodCertContext = NULL;
if(pCertContext == NULL)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto InvalidParameterError;
}
//see if the passed cert has kpi
if(CertGetCertificateContextProperty(
pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
NULL,
&cb))
{
//this means kpi exists, passed cert is good
pGoodCertContext = CertDuplicateCertificateContext(pCertContext);
if (NULL == pGoodCertContext)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertDuplicateCertificateContextError;
}
}
*ppGoodCertContext = pGoodCertContext;
pGoodCertContext = NULL;
hr = S_OK;
ErrorReturn:
if (NULL != pGoodCertContext)
{
CertFreeCertificateContext(pGoodCertContext);
}
return hr;
TRACE_ERROR(InvalidParameterError)
TRACE_ERROR(CertDuplicateCertificateContextError)
}
HRESULT STDMETHODCALLTYPE CCEnroll::SetSignerCertificate(
IN PCCERT_CONTEXT pCertContext)
{
HRESULT hr;
PCCERT_CONTEXT pCertGoodContext = NULL;
EnterCriticalSection(&m_csXEnroll);
hr = GetGoodCertContext(pCertContext, &pCertGoodContext);
if (S_OK != hr)
{
goto GetGoodCertContextError;
}
if(NULL != m_pCertContextSigner)
{
CertFreeCertificateContext(m_pCertContextSigner);
}
m_pCertContextSigner = pCertGoodContext;
hr = S_OK;
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
return hr;
TRACE_ERROR(GetGoodCertContextError)
}
HRESULT
VerifyPrivateKeyArchiveCertificate(
IN PCCERT_CONTEXT pCert)
{
HRESULT hr;
CERT_CHAIN_PARA ChainParams;
CERT_CHAIN_POLICY_PARA ChainPolicy;
CERT_CHAIN_POLICY_STATUS PolicyStatus;
CERT_CHAIN_CONTEXT const *pCertChain = NULL;
char *apszCAXchgOids[] = {szOID_KP_CA_EXCHANGE};
WCHAR *pwszSubject = NULL;
WCHAR *pwszIssuer = NULL;
WCHAR *pwszDesignedSubject = NULL;
//easy check to make sure ca exchange cert issuer and subject
//names are in convention
hr = myCertGetNameString(
pCert,
FALSE,
&pwszSubject);
if (S_OK != hr)
{
goto myCertGetNameStringError;
}
hr = myCertGetNameString(
pCert,
TRUE,
&pwszIssuer);
if (S_OK != hr)
{
goto myCertGetNameStringError;
}
hr = myAddNameSuffix(
pwszIssuer,
wszCNXCHGSUFFIX,
cchCOMMONNAMEMAX_XELIB,
&pwszDesignedSubject);
if (S_OK != hr)
{
goto myAddNameSuffixError;
}
if (0 != wcscmp(pwszSubject, pwszDesignedSubject))
{
//unexpected, they should match
hr = E_INVALIDARG;
goto InvalidArgError;
}
ZeroMemory(&ChainParams, sizeof(ChainParams));
ChainParams.cbSize = sizeof(ChainParams);
ChainParams.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND;
ChainParams.RequestedUsage.Usage.rgpszUsageIdentifier = apszCAXchgOids;
ChainParams.RequestedUsage.Usage.cUsageIdentifier = ARRAYSIZE(apszCAXchgOids);
//get cert chain 1st
if (!MyCertGetCertificateChain(
NULL, //HHCE_CURRENT_USER
pCert, //ca exchange cert
NULL, //use current system time
NULL, //no additional stores
&ChainParams, //chain params
CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, // Make sure that none of the certs in the chain were revoked
NULL, //reserved
&pCertChain))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertGetCertificateChainError;
}
ZeroMemory(&ChainPolicy, sizeof(ChainPolicy));
ChainPolicy.cbSize = sizeof(ChainPolicy);
ChainPolicy.dwFlags = CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;
ZeroMemory(&PolicyStatus, sizeof(PolicyStatus));
PolicyStatus.cbSize = sizeof(PolicyStatus);
PolicyStatus.lChainIndex = -1;
PolicyStatus.lElementIndex = -1;
//verify the chain
if (!MyCertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_BASE,
pCertChain,
&ChainPolicy,
&PolicyStatus))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertVerifyCertificateChainPolicyError;
}
if (S_OK != PolicyStatus.dwError)
{
//chain back to root fails
hr = PolicyStatus.dwError;
goto CertVerifyCertificateChainPolicyError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pCertChain)
{
MyCertFreeCertificateChain(pCertChain);
}
if (NULL != pwszSubject)
{
LocalFree(pwszSubject);
}
if (NULL != pwszDesignedSubject)
{
LocalFree(pwszDesignedSubject);
}
if (NULL != pwszIssuer)
{
LocalFree(pwszIssuer);
}
return hr;
TRACE_ERROR(CertGetCertificateChainError)
TRACE_ERROR(CertVerifyCertificateChainPolicyError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(myCertGetNameStringError)
TRACE_ERROR(myAddNameSuffixError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::SetPrivateKeyArchiveCertificate(
IN PCCERT_CONTEXT pPrivateKeyArchiveCert)
{
HRESULT hr;
PCCERT_CONTEXT pCert = NULL;
if (NULL != pPrivateKeyArchiveCert)
{
//duplicate the cert
pCert = CertDuplicateCertificateContext(pPrivateKeyArchiveCert);
if (NULL == pCert)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertDuplicateCertificateContextError;
}
//verify ca exchange cert
hr = VerifyPrivateKeyArchiveCertificate(pCert);
if (S_OK != hr)
{
goto VerifyPrivateKeyArchiveCertificateError;
}
}
EnterCriticalSection(&m_csXEnroll);
if (NULL != m_PrivateKeyArchiveCertificate)
{
CertFreeCertificateContext(m_PrivateKeyArchiveCertificate);
}
m_PrivateKeyArchiveCertificate = pCert;
pCert = NULL;
LeaveCriticalSection(&m_csXEnroll);
hr = S_OK;
ErrorReturn:
if (NULL != pCert)
{
CertFreeCertificateContext(pCert);
}
return (hr);
TRACE_ERROR(CertDuplicateCertificateContextError)
TRACE_ERROR(VerifyPrivateKeyArchiveCertificateError)
}
PCCERT_CONTEXT STDMETHODCALLTYPE
CCEnroll::GetPrivateKeyArchiveCertificate(void)
{
PCCERT_CONTEXT pCert = NULL;
EnterCriticalSection(&m_csXEnroll);
if (NULL != m_PrivateKeyArchiveCertificate)
{
pCert = CertDuplicateCertificateContext(m_PrivateKeyArchiveCertificate);
}
LeaveCriticalSection(&m_csXEnroll);
return pCert;
}
HRESULT STDMETHODCALLTYPE
CCEnroll::binaryBlobToString(
IN LONG Flags,
IN PCRYPT_DATA_BLOB pblobBinary,
OUT LPWSTR *ppwszString)
{
HRESULT hr;
WCHAR *pwszEncoded = NULL;
DWORD dwEncoded = 0;
while (TRUE)
{
if (!MyCryptBinaryToStringW(
pblobBinary->pbData,
pblobBinary->cbData,
Flags,
pwszEncoded,
&dwEncoded))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto MyCryptBinaryToStringError;
}
if (NULL != pwszEncoded)
{
//done
break;
}
//dwEncoded includes null terminator
pwszEncoded = (WCHAR*)MyCoTaskMemAlloc(dwEncoded * sizeof(WCHAR));
if (NULL == pwszEncoded)
{
hr = E_OUTOFMEMORY;
goto MyCoTaskMemAllocError;
}
}
*ppwszString = pwszEncoded;
pwszEncoded = NULL;
hr = S_OK;
ErrorReturn:
if (NULL != pwszEncoded)
{
MyCoTaskMemFree(pwszEncoded);
}
return hr;
TRACE_ERROR(MyCoTaskMemAllocError)
TRACE_ERROR(MyCryptBinaryToStringError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::stringToBinaryBlob(
IN LONG Flags,
IN LPCWSTR pwszString,
OUT PCRYPT_DATA_BLOB pblobBinary,
OUT LONG *pdwSkip,
OUT LONG *pdwFlags)
{
HRESULT hr;
size_t nLength = wcslen(pwszString);
if (nLength*sizeof(WCHAR) > (DWORD)-1)
goto InvalidArgError;
//init
pblobBinary->pbData = NULL;
pblobBinary->cbData = 0;
while (TRUE)
{
if (!MyCryptStringToBinaryW(
pwszString,
(DWORD)nLength,
Flags,
pblobBinary->pbData,
&pblobBinary->cbData,
(DWORD*)pdwSkip,
(DWORD*)pdwFlags))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto MyCryptStringToBinaryWError;
}
if (NULL != pblobBinary->pbData)
{
//done
break;
}
pblobBinary->pbData = (BYTE*)MyCoTaskMemAlloc(pblobBinary->cbData);
if (NULL == pblobBinary->pbData)
{
hr = E_OUTOFMEMORY;
goto MyCoTaskMemAllocError;
}
}
hr = S_OK;
ErrorReturn:
return hr;
SET_HRESULT(InvalidArgError, E_INVALIDARG);
TRACE_ERROR(MyCryptStringToBinaryWError)
TRACE_ERROR(MyCoTaskMemAllocError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::addExtensionToRequestWStr(
IN LONG Flags,
IN LPCWSTR pwszName,
IN PCRYPT_DATA_BLOB pblobValue)
{
HRESULT hr = S_OK;
CERT_EXTENSION ext;
CERT_EXTENSION *pExt = NULL; //enum 1st
CHAR *pszName = NULL;
//convert wsz oid to sz oid
hr = xeWSZToSZ(pwszName, &pszName);
if (S_OK != hr)
{
goto error;
}
while (NULL != (pExt = EnumStackExtension(pExt, TRUE)))
{
if (0 == strcmp(pszName, pExt->pszObjId))
{
//already had the extension, can't have more than 1
hr = MY_HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
goto error;
}
}
//check to see if it is key usage extension
if (0 == strcmp(pszName, szOID_KEY_USAGE))
{
EnterCriticalSection(&m_csXEnroll);
m_fUseClientKeyUsage = TRUE;
LeaveCriticalSection(&m_csXEnroll);
}
ZeroMemory(&ext, sizeof(ext));
ext.fCritical = Flags;
ext.pszObjId = pszName;
ext.Value = *pblobValue;
if(!CopyAndPushStackExtension(&ext, TRUE))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
error:
if (NULL != pszName)
{
MyCoTaskMemFree(pszName);
}
return hr;
}
HRESULT STDMETHODCALLTYPE
CCEnroll::addAttributeToRequestWStr(
IN LONG /*Flags*/,
IN LPCWSTR pwszName,
IN PCRYPT_DATA_BLOB pblobValue)
{
HRESULT hr = S_OK;
CRYPT_ATTR_BLOB attrBlob;
CRYPT_ATTRIBUTE attr;
CHAR *pszName = NULL;
//convert wsz oid to sz oid
hr = xeWSZToSZ(pwszName, &pszName);
if (S_OK != hr)
{
goto error;
}
ZeroMemory(&attr, sizeof(attr));
attrBlob = *pblobValue;
attr.pszObjId = pszName;
attr.cValue = 1;
attr.rgValue = &attrBlob;
if(!CopyAndPushStackAttribute(&attr, TRUE))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
error:
if (NULL != pszName)
{
MyCoTaskMemFree(pszName);
}
return hr;
}
HRESULT STDMETHODCALLTYPE
CCEnroll::addNameValuePairToRequestWStr(
IN LONG /*Flags*/,
IN LPCWSTR pwszName,
IN LPCWSTR pwszValue)
{
HRESULT hr = S_OK;
assert(pwszName != NULL && pwszValue != NULL);
CRYPT_ENROLLMENT_NAME_VALUE_PAIR nameValuePair =
{const_cast<LPWSTR>(pwszName), const_cast<LPWSTR>(pwszValue)};
CRYPT_ATTR_BLOB blobAttr;
CRYPT_ATTRIBUTE attr = {szOID_ENROLLMENT_NAME_VALUE_PAIR, 1, &blobAttr};
memset(&blobAttr, 0, sizeof(CRYPT_ATTR_BLOB));
hr = xeEncodeNameValuePair(
&nameValuePair,
&blobAttr.pbData,
&blobAttr.cbData);
if (S_OK != hr)
{
goto error;
}
if(!CopyAndPushStackAttribute(&attr, TRUE))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
}
error:
if (NULL != blobAttr.pbData)
{
MyCoTaskMemFree(blobAttr.pbData);
}
return hr;
}
HRESULT STDMETHODCALLTYPE CCEnroll::addBlobPropertyToCertificateWStr(
IN LONG lPropertyId,
IN LONG lFlags,
IN PCRYPT_DATA_BLOB pBlobProp)
{
HRESULT hr;
PPROP_STACK pProp;
PPROP_STACK pPropEle = NULL;
EnterCriticalSection(&m_csXEnroll);
if (NULL == pBlobProp ||
NULL == pBlobProp->pbData ||
0 == pBlobProp->cbData)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto InvalidParameterError;
}
// Don't allow arbitrary properties to be set on the request from a script
// (could be a security risk, we should only allow a small set)
if (0 != m_dwEnabledSafteyOptions && !IsDesiredProperty(lPropertyId))
{
hr = MY_HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
goto InvalidParameterError;
}
//check if the same property exists
pProp = EnumStackProperty(NULL);
while (NULL != pProp)
{
if (pProp->lPropId == lPropertyId)
{
//exists already
hr = MY_HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
goto PropertyExistError;
}
pProp = EnumStackProperty(pProp);
}
pPropEle = (PPROP_STACK)LocalAlloc(LMEM_ZEROINIT, sizeof(PROP_STACK));
if (NULL == pPropEle)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
pPropEle->lPropId = lPropertyId;
pPropEle->lFlags = lFlags;
pPropEle->prop.pbData = (BYTE*)LocalAlloc(LMEM_FIXED, pBlobProp->cbData);
if (NULL == pPropEle->prop.pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
CopyMemory(pPropEle->prop.pbData, pBlobProp->pbData, pBlobProp->cbData);
pPropEle->prop.cbData = pBlobProp->cbData;
//put into stack
pPropEle->pNext = m_pPropStack;
m_pPropStack = pPropEle; //assign m_pPropStack
m_cPropStack++; //increment of m_cPropStack
pPropEle = NULL;
hr = S_OK;
ErrorReturn:
if (NULL != pPropEle)
{
if (NULL != pPropEle->prop.pbData)
{
LocalFree(pPropEle->prop.pbData);
}
LocalFree(pPropEle);
}
LeaveCriticalSection(&m_csXEnroll);
return hr;
TRACE_ERROR(InvalidParameterError)
TRACE_ERROR(PropertyExistError)
TRACE_ERROR(OutOfMemoryError)
}
PPROP_STACK
CCEnroll::EnumStackProperty(PPROP_STACK pProp)
{
EnterCriticalSection(&m_csXEnroll);
if(NULL == pProp)
{
//1st one
pProp = m_pPropStack;
}
else
{
pProp = pProp->pNext;
}
LeaveCriticalSection(&m_csXEnroll);
return pProp;
}
HRESULT STDMETHODCALLTYPE CCEnroll::resetBlobProperties()
{
PPROP_STACK pPropEle;
PPROP_STACK pPropNext;
EnterCriticalSection(&m_csXEnroll);
pPropEle = m_pPropStack;
while (NULL != pPropEle)
{
//save it to temp
pPropNext = EnumStackProperty(pPropEle);
//free the current ele
if (NULL != pPropEle->prop.pbData)
{
LocalFree(pPropEle->prop.pbData);
}
LocalFree(pPropEle);
pPropEle = pPropNext;
}
m_pPropStack = NULL;
m_cPropStack = 0;
LeaveCriticalSection(&m_csXEnroll);
return S_OK;
}
HRESULT
CCEnroll::GetKeyArchivePKCS7(
OUT CRYPT_ATTR_BLOB *pBlobKeyArchivePKCS7)
{
HRESULT hr;
HCRYPTPROV hProv;
HCRYPTKEY hKey = NULL;
BYTE *pBlobPrivateKey = NULL;
DWORD cBlobPrivateKey = 0;
CRYPT_ENCRYPT_MESSAGE_PARA cemp;
ALG_ID algId[] = {CALG_3DES, CALG_RC4, CALG_RC2, ALG_TYPE_ANY};
CRYPT_OID_INFO const *pOidInfo = NULL;
DWORD i = 0;
//init
pBlobKeyArchivePKCS7->pbData = NULL;
pBlobKeyArchivePKCS7->cbData = 0;
EnterCriticalSection(&m_csXEnroll);
//make sure key archival cert is set
assert(NULL != m_PrivateKeyArchiveCertificate);
PCCERT_CONTEXT apCert[] = {m_PrivateKeyArchiveCertificate};
//get user private key
hProv = GetProv(0); //existing key container handle
if (NULL == hProv)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptAcquireContextError;
}
if (NULL == m_hCachedKey)
{
//likely used existing key
if(!CryptGetUserKey(
hProv,
m_keyProvInfo.dwKeySpec,
&hKey))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptGetUserKeyError;
}
}
//export private key
while (TRUE)
{
if (!CryptExportKey(
NULL != hKey ? hKey : m_hCachedKey,
NULL, //don't encrypt
PRIVATEKEYBLOB,
0,
pBlobPrivateKey,
&cBlobPrivateKey))
{
//map to xenroll error
hr = XENROLL_E_KEY_NOT_EXPORTABLE;
goto CryptExportKeyError;
}
if (NULL != pBlobPrivateKey)
{
//done
break;
}
pBlobPrivateKey = (BYTE*)MyCoTaskMemAlloc(cBlobPrivateKey);
if (NULL == pBlobPrivateKey)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
if (NULL == m_hCachedKey)
{
//it could be csp not supporting CRYPT_ARCHIVABLE
//got private key, now let's take care of key permission
if (0x0 == (m_dwGenKeyFlags & CRYPT_EXPORTABLE))
{
// user didn't ask exportable, turn it off
DWORD dwFlags = 0;
DWORD dwSize = sizeof(dwFlags);
if (!CryptGetKeyParam(
hKey,
KP_PERMISSIONS,
(BYTE*)&dwFlags,
&dwSize,
0))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptGetKeyParamError;
}
#if DBG
assert(dwSize = sizeof(dwFlags));
// make sure was on
assert(0x0 != (dwFlags & CRYPT_EXPORT));
#endif
//turn off exportable
dwFlags = dwFlags & (~CRYPT_EXPORT);
if (!CryptSetKeyParam(
hKey,
KP_PERMISSIONS,
(BYTE*)&dwFlags,
0))
{
//hr = MY_HRESULT_FROM_WIN32(GetLastError());
//goto CryptSetKeyParamError;
hr = S_OK; //UNDONE, even ms csps have problem with this
}
}
}
//prepare for encryption
ZeroMemory(&cemp, sizeof(cemp)); //avoid 0 assignment
cemp.cbSize = sizeof(cemp);
cemp.dwMsgEncodingType = PKCS_7_ASN_ENCODING | X509_ASN_ENCODING;
hr = S_OK; //critical init for double while loop
while (ALG_TYPE_ANY != algId[i])
{
pOidInfo = xeCryptFindOIDInfo(
CRYPT_OID_INFO_ALGID_KEY,
&algId[i],
CRYPT_ENCRYPT_ALG_OID_GROUP_ID);
if (NULL != pOidInfo)
{
cemp.ContentEncryptionAlgorithm.pszObjId =
const_cast<char *>(pOidInfo->pszOID);
//encryt into pkcs7
while (TRUE)
{
if (!CryptEncryptMessage(
&cemp,
sizeof(apCert)/sizeof(apCert[0]),
apCert,
pBlobPrivateKey,
cBlobPrivateKey,
pBlobKeyArchivePKCS7->pbData,
&pBlobKeyArchivePKCS7->cbData))
{
//save the 1st error code
hr = MY_HRESULT_FROM_WIN32(GetLastError());
#ifdef DBG
assert(NULL == pBlobKeyArchivePKCS7->pbData);
#endif
break; //break inner while loop
}
if (NULL != pBlobKeyArchivePKCS7->pbData)
{
//done, got encrypted blob
//ignore error from previous alg tries
hr = S_OK;
break;
}
pBlobKeyArchivePKCS7->pbData = (BYTE*)MyCoTaskMemAlloc(
pBlobKeyArchivePKCS7->cbData);
if (NULL == pBlobKeyArchivePKCS7->pbData)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
if (S_OK == hr)
{
//done, out of outer while loop
break;
}
}
++i;
}
if (NULL == pOidInfo)
{
hr = CRYPT_E_NOT_FOUND;
goto CryptElemNotFoundError;
}
if (S_OK != hr)
{
goto CryptEncryptMessageError;
}
hr = S_OK;
ErrorReturn:
//now let's destroy cached key handle
if (NULL != m_hCachedKey)
{
CryptDestroyKey(m_hCachedKey);
m_hCachedKey = NULL; //critical to reset
}
//note, do above before leaving critical section
LeaveCriticalSection(&m_csXEnroll);
if (NULL != pBlobPrivateKey)
{
SecureZeroMemory(pBlobPrivateKey, cBlobPrivateKey);
MyCoTaskMemFree(pBlobPrivateKey);
}
if (NULL != hKey)
{
CryptDestroyKey(hKey);
}
return hr;
TRACE_ERROR(CryptEncryptMessageError)
TRACE_ERROR(CryptAcquireContextError)
TRACE_ERROR(CryptGetUserKeyError)
TRACE_ERROR(CryptExportKeyError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CryptElemNotFoundError)
//TRACE_ERROR(CryptSetKeyParamError)
TRACE_ERROR(CryptGetKeyParamError)
}
HRESULT
GetKeyProvInfoFromCert(
IN PCCERT_CONTEXT pCert,
OUT DWORD *pdwKeySpec,
OUT HCRYPTPROV *phProv)
{
HRESULT hr;
CRYPT_KEY_PROV_INFO *pKeyProvInfo = NULL;
DWORD cb = 0;
HCRYPTPROV hProv = NULL;
if (NULL == pCert || NULL == phProv || NULL == pdwKeySpec)
{
hr = E_INVALIDARG;
goto InvalidArgError;
}
while (TRUE)
{
if(!CertGetCertificateContextProperty(
pCert,
CERT_KEY_PROV_INFO_PROP_ID,
pKeyProvInfo,
&cb))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertGetCertificateContextPropertyError;
}
if (NULL != pKeyProvInfo)
{
//got it, done
break;
}
pKeyProvInfo = (CRYPT_KEY_PROV_INFO*)LocalAlloc(LMEM_FIXED, cb);
if (NULL == pKeyProvInfo)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
if (!CryptAcquireContextU(
&hProv,
pKeyProvInfo->pwszContainerName,
pKeyProvInfo->pwszProvName,
pKeyProvInfo->dwProvType,
pKeyProvInfo->dwFlags))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptAcquireContextUError;
}
*phProv = hProv;
hProv = NULL;
*pdwKeySpec = pKeyProvInfo->dwKeySpec;
hr = S_OK;
ErrorReturn:
if (NULL != pKeyProvInfo)
{
LocalFree(pKeyProvInfo);
}
if (NULL != hProv)
{
CryptReleaseContext(hProv, 0);
}
return hr;
TRACE_ERROR(CryptAcquireContextUError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(CertGetCertificateContextPropertyError)
TRACE_ERROR(InvalidArgError)
}
HRESULT
xeCreateKeyArchivalHashAttribute(
IN CRYPT_HASH_BLOB *pBlobKAHash,
OUT CRYPT_ATTR_BLOB *pBlobKAAttr)
{
HRESULT hr;
BYTE *pbData = NULL;
DWORD cbData = 0;
while (TRUE)
{
if(!CryptEncodeObject(
CRYPT_ASN_ENCODING,
X509_OCTET_STRING,
(void*)pBlobKAHash,
pbData,
&cbData))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CryptEncodeObjectError;
}
if (NULL != pbData)
{
//done
break;
}
pbData = (BYTE*)LocalAlloc(LMEM_FIXED, cbData);
if (NULL == pbData)
{
hr = E_OUTOFMEMORY;
goto LocalAllocError;
}
}
pBlobKAAttr->pbData = pbData;
pBlobKAAttr->cbData = cbData;
pbData = NULL;
hr = S_OK;
ErrorReturn:
if (NULL != pbData)
{
LocalFree(pbData);
}
return hr;
TRACE_ERROR(CryptEncodeObjectError)
TRACE_ERROR(LocalAllocError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::createRequestWStr(
IN LONG Flags,
IN LPCWSTR pwszDNName,
IN LPCWSTR pwszUsage,
OUT PCRYPT_DATA_BLOB pblobRequest)
{
HRESULT hr;
CRYPT_DATA_BLOB blobPKCS10;
CRYPT_ATTR_BLOB blobKeyArchivePKCS7;
ALG_ID rgAlg[2];
PCCRYPT_OID_INFO pOidInfo;
CERT_EXTENSION *rgExt = NULL;
DWORD cExt = 0;
CERT_EXTENSION *pExt = NULL; //for enum 1st
CRYPT_ATTRIBUTE *rgAttr = NULL;
DWORD cAttr = 0;
CRYPT_ATTRIBUTE *pAttr = NULL; //for enum 1st
CRYPT_ATTRIBUTES rgAttributes;
CRYPT_ATTRIBUTE *rgUnauthAttr = NULL; //init
DWORD cUnauthAttr = 0; //init
DWORD cb;
HCRYPTPROV hProvSigner = NULL;
DWORD dwKeySpecSigner = 0;
PCCERT_CONTEXT pCertSigner = NULL; //just init, no free
HCRYPTPROV hRequestProv = NULL;
BYTE *pbSubjectKeyHash = NULL;
DWORD cbSubjectKeyHash = 0;
CRYPT_HASH_BLOB blobKAHash;
CRYPT_ATTR_BLOB blobKAHashAttr;
CRYPT_ATTRIBUTE attrKAHash =
{szOID_ENCRYPTED_KEY_HASH, 1, &blobKAHashAttr};
ZeroMemory(&blobPKCS10, sizeof(blobPKCS10));
ZeroMemory(&blobKeyArchivePKCS7, sizeof(blobKeyArchivePKCS7));
ZeroMemory(&blobKAHash, sizeof(blobKAHash));
ZeroMemory(&blobKAHashAttr, sizeof(blobKAHashAttr));
EnterCriticalSection(&m_csXEnroll);
// BUG 533202: should block key archival when re-using key (from script)
if (0 != m_dwEnabledSafteyOptions && NULL != m_PrivateKeyArchiveCertificate && m_fUseExistingKey) {
hr = E_ACCESSDENIED;
goto AccessDeniedError;
}
m_fNewRequestMethod = TRUE; //critical
m_fOID_V2 = TRUE;
m_fCMCFormat = FALSE;
m_fHonorIncludeSubjectKeyID = FALSE;
switch (Flags)
{
case XECR_CMC:
{
if (NULL != m_pCertContextRenewal &&
NULL != m_pCertContextSigner)
{
//don't support both on yet
hr = MY_HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
goto NotSupportedError;
}
m_fCMCFormat = TRUE;
m_fHonorIncludeSubjectKeyID = TRUE;
// create pkcs 10 first
hr = createPKCS10WStr(
pwszDNName,
pwszUsage, //wszPurpose,
&blobPKCS10);
if(S_OK != hr)
{
goto createPKCS10WStrError;
}
//set it back
m_fCMCFormat = FALSE;
//get all extensions
cb = CountStackExtension(TRUE) * sizeof(CERT_EXTENSION);
if (0 < cb)
{
rgExt = (CERT_EXTENSION*)LocalAlloc(LMEM_FIXED, cb);
if (NULL == rgExt)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
ZeroMemory(rgExt, cb);
while(NULL != (pExt = EnumStackExtension(pExt, TRUE)))
{
rgExt[cExt] = *pExt;
cExt++;
}
}
//get all attributes including namevalue pair
cb = CountStackAttribute(TRUE) * sizeof(CRYPT_ATTRIBUTE);
if (NULL != m_PrivateKeyArchiveCertificate)
{
//add one more attribute to hold encrypted key hash
cb += sizeof(CRYPT_ATTRIBUTE);
}
if (0 < cb)
{
rgAttr = (CRYPT_ATTRIBUTE*)LocalAlloc(LMEM_FIXED, cb);
if (NULL == rgAttr)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
ZeroMemory(rgAttr, cb);
while(NULL != (pAttr = EnumStackAttribute(pAttr, TRUE)))
{
rgAttr[cAttr] = *pAttr;
cAttr++;
}
rgAttributes.rgAttr = rgAttr;
rgAttributes.cAttr = cAttr;
}
if (NULL != m_PrivateKeyArchiveCertificate)
{
hr = GetKeyArchivePKCS7(&blobKeyArchivePKCS7);
if (S_OK != hr)
{
goto GetKeyArchivePKCS7Error;
}
rgUnauthAttr = (CRYPT_ATTRIBUTE*)LocalAlloc(LMEM_FIXED,
sizeof(CRYPT_ATTRIBUTE));
if (NULL == rgUnauthAttr)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
rgUnauthAttr->pszObjId = szOID_ARCHIVED_KEY_ATTR;
rgUnauthAttr->cValue = 1;
rgUnauthAttr->rgValue = &blobKeyArchivePKCS7;
++cUnauthAttr;
//if key archival cert is set, should save the hash
//of the encrypted private key
hr = myCalculateKeyArchivalHash(
blobKeyArchivePKCS7.pbData,
blobKeyArchivePKCS7.cbData,
&blobKAHash.pbData,
&blobKAHash.cbData);
if (S_OK != hr)
{
goto myCalculateKeyArchivalHashError;
}
if (!CertSetCertificateContextProperty(
m_pCertContextPendingRequest, //use pending cert
CERT_ARCHIVED_KEY_HASH_PROP_ID,
0,
&blobKAHash))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertSetCertificateContextPropertyError;
}
hr = xeCreateKeyArchivalHashAttribute(
&blobKAHash,
&blobKAHashAttr);
if (S_OK != hr)
{
goto xeCreateKeyArchivalHashAttributeError;
}
//add this attribute into the array
rgAttr[cAttr] = attrKAHash;
cAttr++;
rgAttributes.rgAttr = rgAttr;
rgAttributes.cAttr = cAttr;
}
//client may set m_HashAlgId but it is not guaranteed
//GetCapiHashAndSigAlgId will determine which one
//is actually used
if (!GetCapiHashAndSigAlgId(rgAlg))
{
hr = NTE_BAD_ALGID;
goto GetCapiHashAndSigAlgIdError;
}
pOidInfo = xeCryptFindOIDInfo(
CRYPT_OID_INFO_ALGID_KEY,
(void*)rgAlg, //point to rgAlg[0]
CRYPT_HASH_ALG_OID_GROUP_ID);
if (NULL == pOidInfo)
{
goto xeCryptFindOIDInfoError;
}
if (NULL != m_pCertContextRenewal)
{
pCertSigner = m_pCertContextRenewal;
}
if (NULL != m_pCertContextSigner)
{
pCertSigner = m_pCertContextSigner;
}
if (NULL != pCertSigner)
{
//get signer key prov info
hr = GetKeyProvInfoFromCert(
pCertSigner,
&dwKeySpecSigner,
&hProvSigner);
if (S_OK != hr)
{
goto GetKeyProvInfoFromCertError;
}
}
//this is CMC, honor anyway
if (m_fIncludeSubjectKeyID)
{
hr = myGetPublicKeyHash(
NULL,
m_pPublicKeyInfo,
&pbSubjectKeyHash,
&cbSubjectKeyHash);
if (S_OK != hr)
{
goto myGetPublicKeyHashError;
}
}
hRequestProv = GetProv(0);
if (NULL == hRequestProv)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto GetProvError;
}
//ok, now call cmc create
hr = BuildCMCRequest(
m_lClientId,
FALSE, //fNestedCMCRequest
blobPKCS10.pbData,
blobPKCS10.cbData,
rgExt,
cExt,
(0 != cAttr) ? &rgAttributes : NULL,
(0 != cAttr) ? 1 : 0,
rgUnauthAttr,
cUnauthAttr,
pbSubjectKeyHash,
cbSubjectKeyHash,
hRequestProv,
m_keyProvInfo.dwKeySpec,
pOidInfo->pszOID,
pCertSigner,
hProvSigner,
dwKeySpecSigner,
NULL, //pOidInfo->pszOID, //this seems to me not necessary because we passed the cert context
&pblobRequest->pbData,
&pblobRequest->cbData);
if (S_OK != hr)
{
goto BuildCMCRequestError;
}
}
break;
case XECR_PKCS7:
if ((NULL == m_pCertContextRenewal &&
NULL == m_pCertContextSigner) ||
NULL != m_PrivateKeyArchiveCertificate)
{
//renew cert is not set, can't make it pkcs7
//pkcs7 can't support key archival
hr = E_INVALIDARG;
goto InvalidArgError;
}
// old method will return pkcs7
hr = createPKCS10WStr(
pwszDNName,
pwszUsage, //wszPurpose,
pblobRequest);
if(S_OK != hr)
{
goto createPKCS10WStrError;
}
break;
case XECR_PKCS10_V1_5:
m_fOID_V2 = FALSE;
//fall through
case XECR_PKCS10_V2_0:
if (NULL != m_PrivateKeyArchiveCertificate)
{
//pkcs10 can't support key archival
hr = E_INVALIDARG;
goto InvalidArgError;
}
m_fHonorRenew = FALSE; //avoid return pkcs7
//for new PKCS10 we allow include subject key id extension
m_fHonorIncludeSubjectKeyID = TRUE;
// call old method
hr = createPKCS10WStr(
pwszDNName,
pwszUsage, //wszPurpose,
pblobRequest);
if(S_OK != hr)
{
goto createPKCS10WStrError;
}
break;
default:
hr = E_INVALIDARG;
goto InvalidArgError;
break;
}
//in all cases, we called createPKCS10WStr
if(m_wszPVKFileName[0] != 0 && !m_fUseExistingKey)
{
//we hold on this until possible cmc is created
GetProv(CRYPT_DELETEKEYSET);
}
hr = S_OK;
ErrorReturn:
m_fNewRequestMethod = FALSE; //critical
m_fOID_V2 = FALSE; //critical for backward compatiability
m_fHonorRenew = TRUE; //critical
m_fHonorIncludeSubjectKeyID = TRUE; //critical for backward compt.
LeaveCriticalSection(&m_csXEnroll);
if (NULL != rgExt)
{
LocalFree(rgExt);
}
if (NULL != rgAttr)
{
LocalFree(rgAttr);
}
if (NULL != rgUnauthAttr)
{
LocalFree(rgUnauthAttr);
}
if (NULL != blobKeyArchivePKCS7.pbData)
{
MyCoTaskMemFree(blobKeyArchivePKCS7.pbData);
}
if (NULL != blobPKCS10.pbData)
{
MyCoTaskMemFree(blobPKCS10.pbData);
}
if (NULL != hProvSigner)
{
CryptReleaseContext(hProvSigner, 0);
}
if (NULL != pbSubjectKeyHash)
{
LocalFree(pbSubjectKeyHash);
}
if (NULL != blobKAHash.pbData)
{
LocalFree(blobKAHash.pbData);
}
if (NULL != blobKAHashAttr.pbData)
{
LocalFree(blobKAHashAttr.pbData);
}
return hr;
TRACE_ERROR(AccessDeniedError);
TRACE_ERROR(createPKCS10WStrError)
TRACE_ERROR(BuildCMCRequestError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(GetCapiHashAndSigAlgIdError)
TRACE_ERROR(GetKeyArchivePKCS7Error)
TRACE_ERROR(xeCryptFindOIDInfoError)
TRACE_ERROR(OutOfMemoryError)
TRACE_ERROR(GetKeyProvInfoFromCertError)
TRACE_ERROR(NotSupportedError)
TRACE_ERROR(GetProvError)
TRACE_ERROR(myGetPublicKeyHashError)
TRACE_ERROR(CertSetCertificateContextPropertyError)
TRACE_ERROR(myCalculateKeyArchivalHashError)
TRACE_ERROR(xeCreateKeyArchivalHashAttributeError)
}
HRESULT
CCEnroll::BlobToBstring(
IN CRYPT_DATA_BLOB *pBlob,
IN DWORD dwFlag,
OUT BSTR *pBString)
{
HRESULT hr;
WCHAR *pwszB64;
DWORD cch;
//init
*pBString = NULL;
// BASE64 encode blob
pwszB64 = NULL;
cch = 0;
while (TRUE)
{
if (!MyCryptBinaryToStringW(
pBlob->pbData,
pBlob->cbData,
dwFlag,
pwszB64,
&cch))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto MyCryptBinaryToStringWError;
}
if (NULL != pwszB64)
{
//got it, done
break;
}
pwszB64 = (WCHAR *)LocalAlloc(LMEM_FIXED, cch * sizeof(WCHAR));
if (NULL == pwszB64)
{
hr = E_OUTOFMEMORY;
goto OutOfMemoryError;
}
}
// SysAllocStringLen
*pBString = SysAllocStringLen(pwszB64, cch);
if(NULL == *pBString)
{
SetLastError(ERROR_OUTOFMEMORY);
hr = E_OUTOFMEMORY;
goto SysAllocStringLenError;
}
hr = S_OK;
ErrorReturn:
if (NULL != pwszB64)
{
LocalFree(pwszB64);
}
return(hr);
TRACE_ERROR(MyCryptBinaryToStringWError)
TRACE_ERROR(SysAllocStringLenError)
TRACE_ERROR(OutOfMemoryError)
}
HRESULT
CCEnroll::BstringToBlob(
IN BSTR bString,
OUT CRYPT_DATA_BLOB *pBlob)
{
HRESULT hr;
assert(NULL != pBlob);
//init
pBlob->pbData = NULL;
pBlob->cbData = 0;
while (TRUE)
{
if (!MyCryptStringToBinaryW(
(LPCWSTR)bString,
SysStringLen(bString),
CRYPT_STRING_ANY,
pBlob->pbData,
&pBlob->cbData,
NULL,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto MyCryptStringToBinaryWError;
}
if (NULL != pBlob->pbData)
{
break; //done
}
pBlob->pbData = (BYTE*)MyCoTaskMemAlloc(pBlob->cbData);
if (NULL == pBlob->pbData)
{
hr = E_OUTOFMEMORY;
goto MyCoTaskMemAllocError;
}
}
hr = S_OK;
ErrorReturn:
return(hr);
TRACE_ERROR(MyCoTaskMemAllocError)
TRACE_ERROR(MyCryptStringToBinaryWError)
}
HRESULT
CCEnroll::createRequestWStrBStr(
IN LONG Flags,
IN LPCWSTR pwszDNName,
IN LPCWSTR pwszUsage,
IN DWORD dwFlag,
OUT BSTR __RPC_FAR *pbstrRequest)
{
HRESULT hr;
CRYPT_DATA_BLOB blobRequest;
memset(&blobRequest, 0, sizeof(blobRequest));
hr = createRequestWStr(Flags, pwszDNName, pwszUsage, &blobRequest);
if (S_OK != hr)
{
goto createRequestWStrError;
}
// convert to bstr
hr = BlobToBstring(&blobRequest, dwFlag, pbstrRequest);
if (S_OK != hr)
{
goto BlobToBstringError;
}
hr = S_OK;
ErrorReturn:
if(NULL != blobRequest.pbData)
{
MyCoTaskMemFree(blobRequest.pbData);
}
return(hr);
TRACE_ERROR(createRequestWStrError)
TRACE_ERROR(BlobToBstringError)
}
HRESULT
CCEnroll::BStringToFile(
IN BSTR bString,
IN LPCWSTR pwszFileName)
{
HRESULT hr;
HANDLE hFile = NULL;
DWORD cb = 0;
LPSTR sz = NULL;
size_t nLength;
sz = MBFromWide(bString);
if(NULL == sz)
{
hr = E_OUTOFMEMORY;
goto MBFromWideError;
}
nLength = strlen(sz);
if (nLength > (DWORD)-1)
goto InvalidArgError;
// open the file
hFile = CreateFileSafely(pwszFileName);
if (NULL == hFile)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CreateFileFileSafelyError;
}
// write the pkcs10
if(!WriteFile(
hFile,
sz,
(DWORD)nLength,
&cb,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto WriteFileError;
}
hr = S_OK;
ErrorReturn:
if(NULL != hFile)
{
CloseHandle(hFile);
}
if(NULL != sz)
{
MyCoTaskMemFree(sz);
}
return(hr);
TRACE_ERROR(CreateFileFileSafelyError)
SET_HRESULT(InvalidArgError, E_INVALIDARG);
TRACE_ERROR(MBFromWideError)
TRACE_ERROR(WriteFileError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::createFileRequestWStr(
IN LONG Flags,
IN LPCWSTR pwszDNName,
IN LPCWSTR pwszUsage,
IN LPCWSTR pwszRequestFileName)
{
HRESULT hr;
BSTR bstrRequest = NULL;
// get the Request
hr = createRequestWStrBStr(
Flags,
pwszDNName,
pwszUsage,
CRYPT_STRING_BASE64REQUESTHEADER,
&bstrRequest);
if(S_OK != hr)
{
goto createRequestWStrBStrError;
}
// save it to file
hr = BStringToFile(bstrRequest, pwszRequestFileName);
if (S_OK != hr)
{
goto BStringToFileError;
}
hr = S_OK;
ErrorReturn:
if(NULL != bstrRequest)
{
SysFreeString(bstrRequest);
}
return(hr);
TRACE_ERROR(createRequestWStrBStrError)
TRACE_ERROR(BStringToFileError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::acceptResponseBlob(
IN PCRYPT_DATA_BLOB pblobResponse)
{
HRESULT hr_old = S_OK;
HRESULT hr;
XCMCRESPONSE *prgResponse = NULL;
DWORD cResponse = 0;
EnterCriticalSection(&m_csXEnroll);
//check in parameter
if (NULL == pblobResponse)
{
hr = E_POINTER;
goto NullPointerError;
}
if (NULL == pblobResponse->pbData ||
0 == pblobResponse->cbData)
{
hr = E_INVALIDARG;
goto InvalidArgError;
}
//make sure init archived key hash
ZeroMemory(&m_blobResponseKAHash, sizeof(m_blobResponseKAHash));
hr_old = ParseCMCResponse(
pblobResponse->pbData,
pblobResponse->cbData,
NULL,
&prgResponse,
&cResponse);
//note, if for any reasons above failed, try pkcs7
if (S_OK == hr_old)
{
if (1 < cResponse)
{
//not supported yet
hr = MY_HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
goto NotSupportedError;
}
#if DBG
//make sure not zero, should be 1
assert(1 == cResponse);
#endif //DBG
//check response status
if (CMC_STATUS_SUCCESS != prgResponse->StatusInfo.dwStatus)
{
hr = prgResponse->StatusInfo.dwStatus; //take status error
goto CMCResponseStatusError;
}
//some code here to get encrypted archived key hash from the response
//and make m_blobResponseKAHash point to the hash data
if (NULL != prgResponse->pbEncryptedKeyHash)
{
m_blobResponseKAHash.pbData = prgResponse->pbEncryptedKeyHash;
m_blobResponseKAHash.cbData = prgResponse->cbEncryptedKeyHash;
}
}
//note, hr_old may not be S_OK, accept the response as pkcs7
hr = acceptPKCS7Blob(pblobResponse);
if (S_OK != hr)
{
if (S_OK != hr_old)
{
//return old error instead of new one
hr = hr_old;
}
goto acceptPKCS7BlobError;
}
hr = S_OK;
ErrorReturn:
//reset hash to zero
ZeroMemory(&m_blobResponseKAHash, sizeof(m_blobResponseKAHash));
LeaveCriticalSection(&m_csXEnroll);
if (NULL != prgResponse)
{
FreeCMCResponse(prgResponse, cResponse);
}
return hr;
TRACE_ERROR(acceptPKCS7BlobError)
TRACE_ERROR(CMCResponseStatusError)
TRACE_ERROR(NotSupportedError)
TRACE_ERROR(InvalidArgError)
TRACE_ERROR(NullPointerError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::acceptFileResponseWStr(
IN LPCWSTR pwszResponseFileName)
{
HRESULT hr;
CRYPT_DATA_BLOB blob;
ZeroMemory(&blob, sizeof(blob));
hr = xeStringToBinaryFromFile(
pwszResponseFileName,
&blob.pbData,
&blob.cbData,
CRYPT_STRING_ANY);
if (S_OK != hr)
{
goto xeStringToBinaryFromFileError;
}
// accept the blob
hr = acceptResponseBlob(&blob);
ErrorReturn:
if (NULL != blob.pbData)
{
LocalFree(blob.pbData);
}
return(hr);
TRACE_ERROR(xeStringToBinaryFromFileError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::getCertContextFromResponseBlob(
IN PCRYPT_DATA_BLOB pblobResponse,
OUT PCCERT_CONTEXT *ppCertContext)
{
HRESULT hr;
if (NULL == ppCertContext)
{
hr = E_POINTER;
goto NullPointerError;
}
//???should check response status?
//response is already in pkcs7
hr = GetEndEntityCert(pblobResponse, FALSE, ppCertContext);
if (S_OK != hr)
{
goto GetEndEntityCertError;
}
hr = S_OK;
ErrorReturn:
return hr;
TRACE_ERROR(NullPointerError)
TRACE_ERROR(GetEndEntityCertError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::getCertContextFromFileResponseWStr(
IN LPCWSTR pwszResponseFileName,
OUT PCCERT_CONTEXT *ppCertContext)
{
HRESULT hr;
CRYPT_DATA_BLOB blobResponse;
ZeroMemory(&blobResponse, sizeof(blobResponse));
// could be any form, binary or base64
hr = xeStringToBinaryFromFile(
pwszResponseFileName,
&blobResponse.pbData,
&blobResponse.cbData,
CRYPT_STRING_ANY);
if (S_OK != hr)
{
goto xeStringToBinaryFromFileError;
}
hr = getCertContextFromResponseBlob(
&blobResponse,
ppCertContext);
if (S_OK != hr)
{
goto getCertContextFromResponseBlobError;
}
hr = S_OK;
ErrorReturn:
if (NULL != blobResponse.pbData)
{
LocalFree(blobResponse.pbData);
}
return hr;
TRACE_ERROR(xeStringToBinaryFromFileError)
TRACE_ERROR(getCertContextFromResponseBlobError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::createPFXWStr(
IN LPCWSTR pwszPassword,
OUT PCRYPT_DATA_BLOB pblobPFX)
{
HRESULT hr;
HCERTSTORE hMemStore = NULL;
DWORD i;
CERT_CHAIN_CONTEXT const *pCertChainContext = NULL;
CERT_CHAIN_PARA CertChainPara;
CERT_SIMPLE_CHAIN *pSimpleChain;
EnterCriticalSection(&m_csXEnroll);
if (NULL == pblobPFX)
{
goto EPointerError;
}
if (NULL == m_pCertContextStatic)
{
hr = E_UNEXPECTED;
goto UnexpectedError;
}
// create a memory store for cert and chain
hMemStore = CertOpenStore(
CERT_STORE_PROV_MEMORY,
X509_ASN_ENCODING,
NULL,
0,
NULL);
if (NULL == hMemStore)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertOpenStoreError;
}
ZeroMemory(&CertChainPara, sizeof(CertChainPara));
CertChainPara.cbSize = sizeof(CertChainPara);
// try to build cert and chain
if (!MyCertGetCertificateChain(
HCCE_CURRENT_USER,
m_pCertContextStatic,
NULL,
NULL,
&CertChainPara,
0,
NULL,
&pCertChainContext))
{
//use 1st hr error
hr = MY_HRESULT_FROM_WIN32(GetLastError());
//try local machine
if (!MyCertGetCertificateChain(
HCCE_LOCAL_MACHINE,
m_pCertContextStatic,
NULL,
NULL,
&CertChainPara,
0,
NULL,
&pCertChainContext))
{
//still use 1st hr
goto MyCertGetCertificateChainError;
}
}
// make sure there is at least 1 simple chain
if (0 == pCertChainContext->cChain)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
goto NoCertificateChainError;
}
//add chain to memory store
pSimpleChain = pCertChainContext->rgpChain[0];
for (i = 0; i < pSimpleChain->cElement; i++)
{
if (!CertAddCertificateContextToStore(
hMemStore,
pSimpleChain->rgpElement[i]->pCertContext,
CERT_STORE_ADD_REPLACE_EXISTING,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CertAddCertificateContextToStoreError;
}
}
pblobPFX->pbData = NULL;
while (TRUE)
{
if (!PFXExportCertStore(
hMemStore,
pblobPFX,
pwszPassword,
EXPORT_PRIVATE_KEYS | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto PFXExportCertStoreError;
}
if (NULL != pblobPFX->pbData)
{
//got it, done
break;
}
pblobPFX->pbData = (BYTE*)MyCoTaskMemAlloc(pblobPFX->cbData);
if (NULL == pblobPFX->pbData)
{
hr = E_OUTOFMEMORY;
goto MyCoTaskMemAllocError;
}
}
hr = S_OK;
ErrorReturn:
LeaveCriticalSection(&m_csXEnroll);
if (pCertChainContext != NULL)
{
MyCertFreeCertificateChain(pCertChainContext);
}
if (NULL != hMemStore)
{
CertCloseStore(hMemStore, CERT_CLOSE_STORE_CHECK_FLAG);
}
return hr;
TRACE_ERROR(UnexpectedError)
TRACE_ERROR(CertOpenStoreError)
TRACE_ERROR(PFXExportCertStoreError)
TRACE_ERROR(MyCoTaskMemAllocError)
TRACE_ERROR(CertAddCertificateContextToStoreError)
TRACE_ERROR(NoCertificateChainError)
TRACE_ERROR(MyCertGetCertificateChainError)
SET_HRESULT(EPointerError, E_POINTER)
}
HRESULT
CCEnroll::createPFXWStrBStr(
IN LPCWSTR pwszPassword,
OUT BSTR __RPC_FAR *pbstrPFX)
{
HRESULT hr;
CRYPT_DATA_BLOB blobPFX;
memset(&blobPFX, 0, sizeof(CRYPT_DATA_BLOB));
hr = createPFXWStr(pwszPassword, &blobPFX);
if (S_OK != hr)
{
goto createPFXWStrError;
}
// convert pfx to bstr
hr = BlobToBstring(&blobPFX, CRYPT_STRING_BASE64, pbstrPFX);
if (S_OK != hr)
{
goto BlobToBstringError;
}
hr = S_OK;
ErrorReturn:
if(NULL != blobPFX.pbData)
{
MyCoTaskMemFree(blobPFX.pbData);
}
return(hr);
TRACE_ERROR(createPFXWStrError)
TRACE_ERROR(BlobToBstringError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::createFilePFXWStr(
IN LPCWSTR pwszPassword,
IN LPCWSTR pwszPFXFileName)
{
HRESULT hr;
HANDLE hFile = NULL;
DWORD cb = 0;
CRYPT_DATA_BLOB blobPFX;
memset(&blobPFX, 0, sizeof(CRYPT_DATA_BLOB));
hr = createPFXWStr(pwszPassword, &blobPFX);
if (S_OK != hr)
{
goto createPFXWStrError;
}
// open the file
hFile = CreateFileSafely(pwszPFXFileName);
if (NULL == hFile)
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto CreateFileFileSafelyError;
}
// write the pkcs10
if(!WriteFile(
hFile,
blobPFX.pbData,
blobPFX.cbData,
&cb,
NULL))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
goto WriteFileError;
}
hr = S_OK;
ErrorReturn:
if(NULL != hFile)
{
CloseHandle(hFile);
}
if(NULL != blobPFX.pbData)
{
MyCoTaskMemFree(blobPFX.pbData);
}
return(hr);
TRACE_ERROR(createPFXWStrError)
TRACE_ERROR(CreateFileFileSafelyError)
TRACE_ERROR(WriteFileError)
}
HRESULT STDMETHODCALLTYPE
CCEnroll::setPendingRequestInfoWStr(
IN LONG lRequestID,
IN LPCWSTR pwszCADNS,
IN LPCWSTR pwszCAName,
IN LPCWSTR pwszFriendlyName
)
{
//------------------------------------------------------------
//
// Define locally-scoped helper functions:
//
//------------------------------------------------------------
CEnrollLocalScope(SetPendingRequestInfoHelper):
// Finds the appropriate cert context to set pending info on using the following algorithm:
// 1) If a hash value HAS NOT been specified, use the cached cert request.
// 2) If a hash value HAS been specified, search the request store for a cert with an equivalent
// hash value and return it. If no such cert can be found, return an error code.
HRESULT GetPendingRequestCertContext(IN HCERTSTORE hStoreRequest,
IN CRYPT_DATA_BLOB hashBlob,
IN PCCERT_CONTEXT pCertContextCachedPendingRequest,
OUT PCCERT_CONTEXT *pCertContextPendingRequest)
{
EquivalentHashCertContextFilter filter(hashBlob);
if (hashBlob.pbData == NULL)
{
// We haven't specified a particular context, use the one we've cached.
*pCertContextPendingRequest = CertDuplicateCertificateContext(pCertContextCachedPendingRequest);
return S_OK;
}
else
{
// Returns the first certificate in the request store with a hash matching
// pHashBlob.
return FilteredCertEnumCertificatesInStore
(hStoreRequest, NULL, &filter, pCertContextPendingRequest);
}
}
DWORD GetPendingInfoBlobSize(IN LONG lRequestID,
IN LPCWSTR pwszCADNS,
IN LPCWSTR pwszCAName,
IN LPCWSTR pwszFriendlyName)
{
assert(pwszCADNS != NULL && pwszCAName != NULL && pwszFriendlyName != NULL);
return (DWORD)(sizeof(lRequestID) + // Request ID
sizeof(DWORD) + // wcslen(pwszCADNS)
sizeof(WCHAR) * (wcslen(pwszCADNS) + 1) + // pwszCADNS
sizeof(DWORD) + // wcslen(pwszCAName)
sizeof(WCHAR) * (wcslen(pwszCAName) + 1) + // pwszCAName
sizeof(DWORD) + // wcslen(pwszFriendlyName)
sizeof(WCHAR) * (wcslen(pwszFriendlyName) + 1) // pwszFriendlyName
);
}
// Combines the supplied pending request information into a CRYPT_DATA_BLOB
// See wincrypt.h for the format.
void MakePendingInfoBlob(IN LONG lRequestID,
IN LPCWSTR pwszCADNS,
IN LPCWSTR pwszCAName,
IN LPCWSTR pwszFriendlyName,
OUT CRYPT_DATA_BLOB pendingInfoBlob)
{
LPBYTE pbBlob;
// None of the inputs should be NULL.
assert(pwszCADNS != NULL && pwszCAName != NULL && pwszFriendlyName != NULL);
// Declare an array of the strings we wish to write to the pending info blob
struct StringsToWrite {
DWORD cc;
LPCWSTR pwsz;
} rgStrings[] = {
{ (DWORD)wcslen(pwszCADNS) + 1, pwszCADNS },
{ (DWORD)wcslen(pwszCAName) + 1, pwszCAName },
{ (DWORD)wcslen(pwszFriendlyName) + 1, pwszFriendlyName }
};
// Write the request ID to the blob
pbBlob = pendingInfoBlob.pbData;
memcpy(pbBlob, &lRequestID, sizeof(lRequestID));
pbBlob += sizeof(lRequestID);
// Write all strings to the blob
for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(rgStrings); dwIndex++)
{
memcpy(pbBlob, &rgStrings[dwIndex].cc, sizeof(rgStrings[dwIndex].cc));
pbBlob += sizeof(rgStrings[dwIndex].cc);
memcpy(pbBlob, rgStrings[dwIndex].pwsz, rgStrings[dwIndex].cc * sizeof(WCHAR));
pbBlob += rgStrings[dwIndex].cc * sizeof(WCHAR);
}
assert(pbBlob == (pendingInfoBlob.pbData + pendingInfoBlob.cbData));
}
CEnrollEndLocalScope;
//------------------------------------------------------------
//
// Begin procedure body.
//
//------------------------------------------------------------
CRYPT_DATA_BLOB pendingInfoBlob;
HCERTSTORE hStoreRequest;
HRESULT hr = S_OK;
PCCERT_CONTEXT pCertContextPendingRequest = NULL;
ZeroMemory(&pendingInfoBlob, sizeof(pendingInfoBlob));
EnterCriticalSection(&m_csXEnroll);
// Input validation:
if (lRequestID < 0 || pwszCADNS == NULL || pwszCAName == NULL)
goto InvalidArgErr;
// NULL is a valid value for pwszFriendlyName. If friendly name is NULL, replace with the empty string:
if (pwszFriendlyName == NULL) { pwszFriendlyName = L""; }
if (NULL == (hStoreRequest = GetStore(StoreREQUEST)) )
goto GetStoreErr;
// Use our locally-scoped helper function to acquire the appropriate certificate context.
if (S_OK != (hr = local.GetPendingRequestCertContext
(hStoreRequest,
m_hashBlobPendingRequest,
m_pCertContextPendingRequest,
&pCertContextPendingRequest)))
goto GetPendingRequestCertContextErr;
// Allocate memory for our pending info blob:
pendingInfoBlob.cbData = local.GetPendingInfoBlobSize
(lRequestID,
pwszCADNS,
pwszCAName,
pwszFriendlyName);
pendingInfoBlob.pbData = (LPBYTE)LocalAlloc(LPTR, pendingInfoBlob.cbData);
if (NULL == pendingInfoBlob.pbData)
goto MemoryErr;
// Combine our arguments into a "pending info" blob.
local.MakePendingInfoBlob
(lRequestID,
pwszCADNS,
pwszCAName,
pwszFriendlyName,
pendingInfoBlob);
// Use our pending info blob to assign the certificate context property.
if (!CertSetCertificateContextProperty
(pCertContextPendingRequest,
CERT_ENROLLMENT_PROP_ID,
0,
&pendingInfoBlob))
{
// Failed to set the context property.
goto CertSetCertificateContextPropertyErr;
}
// We've completed successfully.
hr = S_OK;
CommonReturn:
if (NULL != pendingInfoBlob.pbData) { LocalFree(pendingInfoBlob.pbData); }
if (NULL != pCertContextPendingRequest) { CertFreeCertificateContext(pCertContextPendingRequest); }
LeaveCriticalSection(&m_csXEnroll);
return hr;
ErrorReturn:
goto CommonReturn;
SET_HRESULT(CertSetCertificateContextPropertyErr, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(GetPendingRequestCertContextErr, hr);
SET_HRESULT(GetStoreErr, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(InvalidArgErr, E_INVALIDARG);
SET_HRESULT(MemoryErr, E_OUTOFMEMORY);
}
//--------------------------------------------------------------------------------
//
// THIS METHOD IS NOT SAFE FOR SCRIPTING
//
//--------------------------------------------------------------------------------
HRESULT STDMETHODCALLTYPE
CCEnroll::enumPendingRequestWStr(
IN LONG lIndex,
IN LONG lDesiredProperty,
OUT LPVOID ppProperty
)
{
//------------------------------------------------------------
//
// Define locally scoped helper functions.
//
//------------------------------------------------------------
CEnrollLocalScope(EnumPendingRequestHelper):
CRYPT_DATA_BLOB dataBlob;
HRESULT GetContextPropertySimple(PCCERT_CONTEXT pCertContext, DWORD dwPropID)
{
BOOL fDone = FALSE;
dataBlob.pbData = NULL;
dataBlob.cbData = 0x150;
do {
if (dataBlob.pbData != NULL) { LocalFree(dataBlob.pbData); }
dataBlob.pbData = (LPBYTE)LocalAlloc(LPTR, dataBlob.cbData);
if (dataBlob.pbData == NULL) { return E_OUTOFMEMORY; }
if (!CertGetCertificateContextProperty
(pCertContext,
dwPropID,
(LPVOID)dataBlob.pbData,
&(dataBlob.cbData)))
{
if (GetLastError() != ERROR_MORE_DATA)
return MY_HRESULT_FROM_WIN32(GetLastError());
}
else
{
fDone = TRUE;
}
} while (!fDone);
return S_OK;
}
// Extracts the next packed string from our pending info blob.
// If pbString is non-NULL, it must be large enough to hold the entire string.
LPBYTE GetNextString(IN LPBYTE pbBlob, OUT DWORD *pcbSize, OUT LPBYTE pbString) {
DWORD dwSize;
memcpy(&dwSize, pbBlob, sizeof(DWORD));
dwSize *= sizeof(WCHAR); // Convert to count in bytes.
if (NULL != pcbSize) {
*pcbSize = dwSize;
}
pbBlob += sizeof(DWORD);
if (NULL != pbString) {
memcpy(pbString, pbBlob, dwSize);
}
pbBlob += dwSize;
return pbBlob;
}
HRESULT getRequestID(PCCERT_CONTEXT pCertContext, long *pplProperty) {
HRESULT hr;
if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID)))
*pplProperty = *((long *)dataBlob.pbData);
return hr;
}
HRESULT getCAName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) {
DWORD dwSize;
HRESULT hr;
LPBYTE pb;
if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID)))
{
pb = dataBlob.pbData + sizeof(DWORD); // pb points to DNS Name blob
pb = GetNextString(pb, NULL, NULL); // pb points to CA Name blob
GetNextString(pb, &dwSize, NULL); // dwSize = size in chars of CA Name
// If pbData is NULL, we're just doing a size check.
if (pDataBlobProperty->pbData != NULL)
{
if (pDataBlobProperty->cbData < dwSize)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
else
{
GetNextString(pb, NULL, pDataBlobProperty->pbData);
}
}
pDataBlobProperty->cbData = dwSize;
}
return hr;
}
HRESULT getCADNSName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) {
DWORD dwSize;
HRESULT hr;
LPBYTE pb;
if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID)))
{
pb = dataBlob.pbData + sizeof(DWORD); // pb points to DNS Name blob
GetNextString(pb, &dwSize, NULL); // dwSize = size in chars of CA Name
// If pbData is NULL, we're just doing a size check.
if (pDataBlobProperty->pbData != NULL)
{
if (pDataBlobProperty->cbData < dwSize)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
else
{
GetNextString(pb, NULL, pDataBlobProperty->pbData);
}
}
pDataBlobProperty->cbData = dwSize;
}
return hr;
}
HRESULT getCAFriendlyName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) {
DWORD dwSize;
HRESULT hr;
LPBYTE pb;
if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_ENROLLMENT_PROP_ID)))
{
// Set pb to point to the start of the CA name blob
pb = dataBlob.pbData + sizeof(DWORD); // pb points to DNS Name blob
pb = GetNextString(pb, NULL, NULL); // pb points to CA Name blob
pb = GetNextString(pb, NULL, NULL); // pb points to Friendly Name blob
// dwSize <-- size in chars of CA Name
GetNextString(pb, &dwSize, NULL);
// If pbData is NULL, we're just doing a size check.
if (pDataBlobProperty->pbData != NULL)
{
if (pDataBlobProperty->cbData < dwSize)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
else
{
GetNextString(pb, NULL, pDataBlobProperty->pbData);
}
}
pDataBlobProperty->cbData = dwSize;
}
return hr;
}
HRESULT getHash(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) {
HRESULT hr;
if (S_OK == (hr = GetContextPropertySimple(pCertContext, CERT_HASH_PROP_ID)))
{
// If pbData is NULL, we're just doing a size check.
if (pDataBlobProperty->pbData != NULL)
{
if (pDataBlobProperty->cbData < dataBlob.cbData)
{
hr = MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA);
}
else
{
memcpy(pDataBlobProperty->pbData, dataBlob.pbData, dataBlob.cbData);
}
}
pDataBlobProperty->cbData = dataBlob.cbData;
}
return hr;
}
HRESULT getDate(PCCERT_CONTEXT pCertContext, PFILETIME pftProperty) {
*pftProperty = pCertContext->pCertInfo->NotAfter;
return S_OK;
}
HRESULT getTemplateName(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) {
CERT_NAME_VALUE *pCertTemplateNameValue = NULL;
DWORD cbCertTemplateNameValue;
DWORD cbRequired = 0;
HRESULT hr = S_OK;
PCERT_EXTENSION pCertTemplateExtension = NULL;
if (NULL == (pCertTemplateExtension = CertFindExtension
(szOID_ENROLL_CERTTYPE_EXTENSION,
pCertContext->pCertInfo->cExtension,
pCertContext->pCertInfo->rgExtension)))
return E_INVALIDARG;
if (!CryptDecodeObject
(pCertContext->dwCertEncodingType,
X509_UNICODE_ANY_STRING,
pCertTemplateExtension->Value.pbData,
pCertTemplateExtension->Value.cbData,
0,
NULL,
&cbCertTemplateNameValue) || (cbCertTemplateNameValue == 0))
goto CryptDecodeObjectErr;
pCertTemplateNameValue = (CERT_NAME_VALUE *)LocalAlloc(LPTR, cbCertTemplateNameValue);
if (NULL == pCertTemplateNameValue)
goto MemoryErr;
if (!CryptDecodeObject
(pCertContext->dwCertEncodingType,
X509_UNICODE_ANY_STRING,
pCertTemplateExtension->Value.pbData,
pCertTemplateExtension->Value.cbData,
0,
(void *)(pCertTemplateNameValue),
&cbCertTemplateNameValue))
goto CryptDecodeObjectErr;
cbRequired = sizeof(WCHAR) * (DWORD)(wcslen((LPWSTR)(pCertTemplateNameValue->Value.pbData)) + 1);
if (NULL != pDataBlobProperty->pbData)
{
// Make sure we've allocated a large enough buffer:
if (pDataBlobProperty->cbData < cbRequired) { goto MoreDataErr; }
// Write the template name to the OUT param:
wcscpy((LPWSTR)pDataBlobProperty->pbData, (LPWSTR)(pCertTemplateNameValue->Value.pbData));
}
hr = S_OK;
CommonReturn:
// Assign the size of the template name to the cb of the OUT param.
// This should be done for all code paths.
pDataBlobProperty->cbData = cbRequired;
// Free resources:
if (NULL != pCertTemplateNameValue) { LocalFree(pCertTemplateNameValue); }
return hr;
ErrorReturn:
goto CommonReturn;
SET_HRESULT(CryptDecodeObjectErr, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(MoreDataErr, MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA));
SET_HRESULT(MemoryErr, E_OUTOFMEMORY);
}
HRESULT getTemplateOID(PCCERT_CONTEXT pCertContext, PCRYPT_DATA_BLOB pDataBlobProperty) {
CERT_TEMPLATE_EXT *pCertTemplateExt = NULL;
DWORD cbCertTemplateExt = 0;
DWORD cbRequired = 0;
HRESULT hr;
LPWSTR pwszOID = NULL;
PCERT_EXTENSION pCertExtension = NULL;
if (NULL == (pCertExtension = CertFindExtension
(szOID_CERTIFICATE_TEMPLATE,
pCertContext->pCertInfo->cExtension,
pCertContext->pCertInfo->rgExtension)))
return E_INVALIDARG;
if (FALSE == CryptDecodeObject
(pCertContext->dwCertEncodingType,
X509_CERTIFICATE_TEMPLATE,
pCertExtension->Value.pbData,
pCertExtension->Value.cbData,
0,
NULL,
&cbCertTemplateExt) || (cbCertTemplateExt == 0))
goto CryptDecodeObjectErr;
pCertTemplateExt = (CERT_TEMPLATE_EXT *)LocalAlloc(LPTR, cbCertTemplateExt);
if (NULL == pCertTemplateExt)
goto MemoryErr;
if (FALSE == CryptDecodeObject
(pCertContext->dwCertEncodingType,
X509_CERTIFICATE_TEMPLATE,
pCertExtension->Value.pbData,
pCertExtension->Value.cbData,
0,
(void *)(pCertTemplateExt),
&cbCertTemplateExt))
goto CryptDecodeObjectErr;
cbRequired = sizeof(WCHAR) * (DWORD)(strlen(pCertTemplateExt->pszObjId) + 1);
// See if we're just doing a size check:
if (NULL != pDataBlobProperty->pbData)
{
// Make sure we've allocated a large enough buffer:
if (pDataBlobProperty->cbData < cbRequired) { goto MoreDataErr; }
// Convert the OID to a LPWSTR:
pwszOID = WideFromMB(pCertTemplateExt->pszObjId);
if (NULL == pwszOID)
goto WideFromMBErr;
// Write the template OID to the OUT param:
wcscpy((LPWSTR)pDataBlobProperty->pbData, pwszOID);
}
hr = S_OK;
CommonReturn:
// Assign the size of the OID to the cb of the OUT param.
// This should be done for all code paths.
pDataBlobProperty->cbData = cbRequired;
// Free resources:
if (NULL != pCertTemplateExt) { LocalFree(pCertTemplateExt); }
if (NULL != pwszOID) { MyCoTaskMemFree(pwszOID); }
return hr;
ErrorReturn:
goto CommonReturn;
SET_HRESULT(CryptDecodeObjectErr, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(MemoryErr, E_OUTOFMEMORY);
SET_HRESULT(MoreDataErr, MY_HRESULT_FROM_WIN32(ERROR_MORE_DATA));
SET_HRESULT(WideFromMBErr, MY_HRESULT_FROM_WIN32(GetLastError()));
}
HRESULT getVersion(PCCERT_CONTEXT pCertContext, long *plVersion) {
CERT_TEMPLATE_EXT *pCertTemplateExt = NULL;
DWORD cbCertTemplateExt = 0;
HRESULT hr;
PCERT_EXTENSION pCertExtension = NULL;
if (NULL == (pCertExtension = CertFindExtension
(szOID_CERTIFICATE_TEMPLATE,
pCertContext->pCertInfo->cExtension,
pCertContext->pCertInfo->rgExtension)))
return E_INVALIDARG;
if (FALSE == CryptDecodeObject
(pCertContext->dwCertEncodingType,
X509_CERTIFICATE_TEMPLATE,
pCertExtension->Value.pbData,
pCertExtension->Value.cbData,
0,
NULL,
&cbCertTemplateExt) || (cbCertTemplateExt == 0))
goto CryptDecodeObjectErr;
pCertTemplateExt = (CERT_TEMPLATE_EXT *)LocalAlloc(LPTR, cbCertTemplateExt);
if (NULL == pCertTemplateExt)
goto MemoryErr;
if (FALSE == CryptDecodeObject
(pCertContext->dwCertEncodingType,
X509_CERTIFICATE_TEMPLATE,
pCertExtension->Value.pbData,
pCertExtension->Value.cbData,
0,
(void *)(pCertTemplateExt),
&cbCertTemplateExt))
goto CryptDecodeObjectErr;
*plVersion = (long)pCertTemplateExt->dwMajorVersion;
hr = S_OK;
CommonReturn:
// Free resources:
if (NULL != pCertTemplateExt) { LocalFree(pCertTemplateExt); }
return hr;
ErrorReturn:
goto CommonReturn;
SET_HRESULT(CryptDecodeObjectErr, MY_HRESULT_FROM_WIN32(GetLastError()));
SET_HRESULT(MemoryErr, E_OUTOFMEMORY);
}
void InitLocalScope() {
dataBlob.cbData = 0;
dataBlob.pbData = NULL;
}
void FreeLocalScope() { if (dataBlob.pbData != NULL) { LocalFree(dataBlob.pbData); } }
CEnrollEndLocalScope;
//------------------------------------------------------------
//
// Begin procedure body.
//
//------------------------------------------------------------
// FIXME: index is 0 based, correct?
// m_dwLastPendingRequestIndex = 0;
// m_pCertContextLastEnumerated
HCERTSTORE hStoreRequest = NULL;
HRESULT hr = S_OK;
PCCERT_CONTEXT pCertContext = NULL;
// Input validiation:
if (lIndex != XEPR_ENUM_FIRST && (lIndex < 0 || (ppProperty == NULL)))
return E_INVALIDARG;
if (0 != m_dwEnabledSafteyOptions) // not safe for scripting
return E_ACCESSDENIED;
// Init:
local.InitLocalScope();
EnterCriticalSection(&m_csXEnroll);
if ( NULL == (hStoreRequest = GetStore(StoreREQUEST)) )
goto ErrorCertOpenRequestStore;
// If we're passed the ENUM_FIRST flag, reconstruct a snapshot of the request store.
//
if (lIndex == XEPR_ENUM_FIRST)
{
if (NULL != this->m_pPendingRequestTable) { delete this->m_pPendingRequestTable; }
this->m_pPendingRequestTable = new PendingRequestTable;
if (NULL == this->m_pPendingRequestTable)
goto MemoryErr;
if (S_OK != (hr = this->m_pPendingRequestTable->construct(hStoreRequest)))
goto ErrorConstructPendingTable;
// All done, return.
goto CommonReturn;
}
// We want the lIndex'th element the request store.
// First, ensure that the enumeration is initialized:
if (NULL == m_pPendingRequestTable)
goto PointerErr;
// Index past the end of the table.
if (this->m_pPendingRequestTable->size() <= (DWORD)lIndex)
{
hr = MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND);
goto ErrorReturn;
}
pCertContext = (*this->m_pPendingRequestTable)[(DWORD)lIndex];
switch (lDesiredProperty)
{
case XEPR_REQUESTID: hr = local.getRequestID (pCertContext, (long *)ppProperty); break;
case XEPR_CANAME: hr = local.getCAName (pCertContext, (PCRYPT_DATA_BLOB)ppProperty); break;
case XEPR_CAFRIENDLYNAME: hr = local.getCAFriendlyName (pCertContext, (PCRYPT_DATA_BLOB)ppProperty); break;
case XEPR_CADNS: hr = local.getCADNSName (pCertContext, (PCRYPT_DATA_BLOB)ppProperty); break;
case XEPR_DATE: hr = local.getDate (pCertContext, (PFILETIME)ppProperty); break;
case XEPR_V1TEMPLATENAME: hr = local.getTemplateName (pCertContext, (PCRYPT_DATA_BLOB)ppProperty); break;
case XEPR_V2TEMPLATEOID: hr = local.getTemplateOID (pCertContext, (PCRYPT_DATA_BLOB)ppProperty); break;
case XEPR_VERSION: hr = local.getVersion (pCertContext, (long *)ppProperty); break;
case XEPR_HASH: hr = local.getHash (pCertContext, (PCRYPT_DATA_BLOB)ppProperty); break;
default:
hr = E_INVALIDARG;
}
CommonReturn:
local.FreeLocalScope();
LeaveCriticalSection(&m_csXEnroll);
return hr;
ErrorReturn:
goto CommonReturn;
SET_HRESULT(MemoryErr, E_OUTOFMEMORY);
SET_HRESULT(PointerErr, E_POINTER);
TRACE_ERROR(ErrorCertOpenRequestStore);
TRACE_ERROR(ErrorConstructPendingTable);
}
HRESULT STDMETHODCALLTYPE
CCEnroll::removePendingRequestWStr
(IN CRYPT_DATA_BLOB thumbPrintBlob
)
{
EquivalentHashCertContextFilter equivHashFilter(thumbPrintBlob);
PendingCertContextFilter pendingCertFilter;
// combinedFilter now only allows PENDING requests which match the specified thumbprint.
CompositeCertContextFilter combinedFilter(&equivHashFilter, &pendingCertFilter);
HCERTSTORE hStoreRequest = NULL;
HRESULT hr;
PCCERT_CONTEXT pCertContext = NULL;
EnterCriticalSection(&m_csXEnroll);
// Input validation.
if (NULL == thumbPrintBlob.pbData)
{
hr = E_INVALIDARG;
goto ErrorReturn;
}
if ( NULL == (hStoreRequest = GetStore(StoreREQUEST)) )
{
hr = E_UNEXPECTED;
goto ErrorReturn;
}
if (S_OK != (hr = FilteredCertEnumCertificatesInStore
(hStoreRequest,
NULL,
&combinedFilter,
&pCertContext)))
goto ErrorReturn;
if (!CertDeleteCertificateFromStore(pCertContext))
{
hr = MY_HRESULT_FROM_WIN32(GetLastError());
// CertDeleteCertificateFromStore *always* deletes the cert context.
pCertContext = NULL;
goto ErrorReturn;
}
pCertContext = NULL;
hr = S_OK;
CommonReturn:
LeaveCriticalSection(&m_csXEnroll);
return hr;
ErrorReturn:
if (pCertContext != NULL) { CertFreeCertificateContext(pCertContext); }
goto CommonReturn;
}
HRESULT FilteredCertEnumCertificatesInStore(IN HCERTSTORE hStore,
IN PCCERT_CONTEXT pCertContext,
IN CertContextFilter *pFilter,
OUT PCCERT_CONTEXT *pCertContextNext)
{
BOOL fFilterResult;
HRESULT hr = S_OK;
PCCERT_CONTEXT pCertContextPrev = pCertContext;
while (NULL != (pCertContext = CertEnumCertificatesInStore(hStore, pCertContextPrev)))
{
if (S_OK != (hr = pFilter->accept(pCertContext, &fFilterResult)))
return hr;
if (fFilterResult) // We've found the next cert context in the filtered enumeration.
{
*pCertContextNext = pCertContext;
return S_OK;
}
pCertContextPrev = pCertContext;
}
return MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND);
}
static LPVOID MyLocalAlloc(ULONG cb) {
return((LPVOID) LocalAlloc(LPTR, (UINT) cb));
}
static LPVOID MyLocalRealloc(LPVOID ptr, ULONG cb) {
return((LPVOID) LocalReAlloc((HLOCAL) ptr, (UINT) cb, LMEM_MOVEABLE));
}
static void MyLocalFree(LPVOID ptr) {
LocalFree((HLOCAL) ptr);
}
//
// From xtan, an explanation of the "no-COM" APIs:
// "In early time xenroll interface IDs were not in uuid.lib so this
// was a convenient C interface for people to get xenroll COM interfaces without
// call CoCreateInstance. It is not in MSDN but it is in SDK headers.
// I created the same API for consistency when I created ICEnroll4."
//
void * WINAPI PGetIEnrollNoCOM(const IID &iid) {
void * pvoid = NULL;
IClassFactory * pIClassFactory = NULL;
HRESULT hr = S_OK;
MyCoTaskMemAlloc = MyLocalAlloc;
MyCoTaskMemFree = MyLocalFree;
MyCoTaskMemRealloc = MyLocalRealloc;
if( S_OK != (hr = DllGetClassObject(CLSID_CEnroll2, IID_IClassFactory, (void **) &pIClassFactory)) ) {
pIClassFactory = NULL;
}
else if( S_OK != (hr = pIClassFactory->CreateInstance(NULL, iid, &pvoid)) ) {
pvoid = NULL;
}
if(pIClassFactory != NULL) {
pIClassFactory->Release();
pIClassFactory = NULL;
}
SetLastError(hr);
return(pvoid);
}
IEnroll * WINAPI PIEnrollGetNoCOM(void)
{
return( (IEnroll *) PGetIEnrollNoCOM(IID_IEnroll) );
}
IEnroll2 * WINAPI PIEnroll2GetNoCOM(void)
{
return( (IEnroll2 *) PGetIEnrollNoCOM(IID_IEnroll2) );
}
IEnroll4 * WINAPI PIEnroll4GetNoCOM(void)
{
return( (IEnroll4 *) PGetIEnrollNoCOM(IID_IEnroll4) );
}
HRESULT PendingRequestTable::resize(DWORD dwNewSize)
{
TableElem * newTable = NULL;
if (dwNewSize <= 0)
return E_INVALIDARG;
newTable = (TableElem *)LocalAlloc(LPTR, sizeof(TableElem) * dwNewSize);
if (NULL == newTable)
return E_OUTOFMEMORY;
if (NULL != this->table)
{
memcpy(newTable, this->table, this->dwElemSize * sizeof(TableElem));
LocalFree(this->table);
}
this->dwElemSize = dwNewSize;
this->table = newTable;
return S_OK;
}
HRESULT PendingRequestTable::add(TableElem tePendingRequest)
{
HRESULT hr;
if (this->dwElemCount > this->dwElemSize) { return E_UNEXPECTED; }
else if (this->dwElemCount == this->dwElemSize)
{
// Need to allocate more memory:
DWORD dwNewSize = this->dwElemSize < 100 ? 100 : this->dwElemSize * 2;
if (S_OK != (hr = this->resize(dwNewSize)))
return hr;
}
this->table[this->dwElemCount++] = tePendingRequest;
return S_OK;
}
PendingRequestTable::PendingRequestTable() : table(NULL), dwElemSize(0), dwElemCount(0)
{ }
PendingRequestTable::~PendingRequestTable()
{
if (NULL != this->table)
{
for (DWORD dwIndex = 0; dwIndex < dwElemCount; dwIndex++)
{
CertFreeCertificateContext(this->table[dwIndex].pCertContext);
}
LocalFree(this->table);
}
}
HRESULT PendingRequestTable::construct(HCERTSTORE hStore)
{
HRESULT hr = S_OK;
PendingCertContextFilter pendingFilter;
PCCERT_CONTEXT pCertContext = NULL;
PCCERT_CONTEXT pCertContextPrev = NULL;
TableElem tePendingRequest;
// Enumerate all pending cert contexts, and add them to our table:
for (DWORD dwIndex = 0; TRUE; dwIndex++)
{
if (S_OK != (hr = FilteredCertEnumCertificatesInStore
(hStore,
pCertContextPrev,
&pendingFilter,
&pCertContext)))
break;
tePendingRequest.pCertContext = CertDuplicateCertificateContext(pCertContext);
this->add(tePendingRequest);
pCertContextPrev = pCertContext;
}
return hr == MY_HRESULT_FROM_WIN32(CRYPT_E_NOT_FOUND) ? S_OK : hr;
}