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.
 
 
 
 
 
 

1875 lines
52 KiB

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Microsoft Windows, Copyright (C) Microsoft Corporation, 2000
File: EnvelopedData.cpp
Content: Implementation of CEnvelopedData.
History: 11-15-99 dsie created
------------------------------------------------------------------------------*/
#include "StdAfx.h"
#include "CAPICOM.h"
#include "EnvelopedData.h"
#include "Common.h"
#include "Convert.h"
#include "CertHlpr.h"
#include "MsgHlpr.h"
#include "SignHlpr.h"
#include "Settings.h"
////////////////////////////////////////////////////////////////////////////////
//
// Local functions.
//
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : SelectRecipientCertCallback
Synopsis : Callback routine for CryptUIDlgSelectCertificateW() API for
recipient's cert selection.
Parameter: See CryptUI.h for defination.
Remark : Filter out any cert that is not time valid.
------------------------------------------------------------------------------*/
static BOOL WINAPI SelectRecipientCertCallback (PCCERT_CONTEXT pCertContext,
BOOL * pfInitialSelectedCert,
void * pvCallbackData)
{
int nValidity = 0;
//
// Check cert time validity.
//
if (0 != (nValidity = ::CertVerifyTimeValidity(NULL, pCertContext->pCertInfo)))
{
DebugTrace("Info: SelectRecipientCertCallback() - invalid time (%s).\n",
nValidity < 0 ? "not yet valid" : "expired");
return FALSE;
}
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////
//
// CEnvelopedData
//
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : SetKeyLength
Synopsis : Setup the symetric encryption key length.
Parameter: HCRYPTPROV hCryptProv - CSP handle.
CRYPT_ALGORITHM_IDENTIFIER EncryptAlgorithm - Encryption algorithm.
CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength - Key length.
void ** pAuxInfo - Receive NULL or allocated and initialized
aux info structure.
Remark :
------------------------------------------------------------------------------*/
static HRESULT SetKeyLength (
HCRYPTPROV hCryptProv,
CRYPT_ALGORITHM_IDENTIFIER EncryptAlgorithm,
CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength,
void ** ppAuxInfo)
{
HRESULT hr = S_OK;
ALG_ID AlgID = 0;
PROV_ENUMALGS_EX peex;
CMSG_RC2_AUX_INFO * pRC2AuxInfo = NULL;
CMSG_RC4_AUX_INFO * pRC4AuxInfo = NULL;
DebugTrace("Entering SetKeyLength().\n");
//
// Sanity check.
//
ATLASSERT(hCryptProv);
ATLASSERT(ppAuxInfo);
//
// Initialize.
//
*ppAuxInfo = (void *) NULL;
//
// Get ALG_ID.
//
if (FAILED(hr = ::OIDToAlgID(EncryptAlgorithm.pszObjId, &AlgID)))
{
DebugTrace("Error [%#x]: OIDToAlgID() failed.\n", hr);
goto ErrorExit;
}
//
// Get algorithm capability from CSP.
//
if (FAILED(::IsAlgSupported(hCryptProv, AlgID, &peex)))
{
hr = CAPICOM_E_NOT_SUPPORTED;
DebugTrace("Error: requested encryption algorithm is not available.\n");
goto ErrorExit;
}
//
// Setup AuxInfo for RC2.
//
if (CALG_RC2 == AlgID)
{
//
// Allocate and intialize memory for RC2 AuxInfo structure.
//
if (!(pRC2AuxInfo = (CMSG_RC2_AUX_INFO *) ::CoTaskMemAlloc(sizeof(CMSG_RC2_AUX_INFO))))
{
hr = E_OUTOFMEMORY;
DebugTrace("Error: out of memory.\n");
goto ErrorExit;
}
::ZeroMemory(pRC2AuxInfo, sizeof(CMSG_RC2_AUX_INFO));
pRC2AuxInfo->cbSize = sizeof(CMSG_RC2_AUX_INFO);
//
// Determine key length requested.
//
if (CAPICOM_ENCRYPTION_KEY_LENGTH_MAXIMUM == KeyLength)
{
pRC2AuxInfo->dwBitLen = peex.dwMaxLen;
}
else if (CAPICOM_ENCRYPTION_KEY_LENGTH_40_BITS == KeyLength)
{
if (peex.dwMinLen <= 40 && 40 <= peex.dwMaxLen)
{
pRC2AuxInfo->dwBitLen = 40;
}
else
{
hr = CAPICOM_E_NOT_SUPPORTED;
DebugTrace("Error [%#x]: 40-bits encryption is not available.\n", hr);
goto ErrorExit;
}
}
else if (CAPICOM_ENCRYPTION_KEY_LENGTH_56_BITS == KeyLength)
{
if (peex.dwMinLen <= 56 && 56 <= peex.dwMaxLen)
{
pRC2AuxInfo->dwBitLen = 56;
}
else
{
hr = CAPICOM_E_NOT_SUPPORTED;
DebugTrace("Error [%#x]: 56-bits encryption is not available.\n", hr);
goto ErrorExit;
}
}
else if (CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS == KeyLength)
{
if (peex.dwMinLen <= 128 && 128 <= peex.dwMaxLen)
{
pRC2AuxInfo->dwBitLen = 128;
}
else
{
hr = CAPICOM_E_NOT_SUPPORTED;
DebugTrace("Error [%#x]: 128-bits encryption is not available.\n", hr);
goto ErrorExit;
}
}
else
{
//
// Should never get to here.
//
hr = CAPICOM_E_INTERNAL;
DebugTrace("Error [%#x]: Unknown key length (%d).\n", hr, KeyLength);
goto ErrorExit;
}
//
// Return RC2 AuxInfo pointer to caller.
//
*ppAuxInfo = (void *) pRC2AuxInfo;
}
else if (CALG_RC4 == AlgID)
{
//
// Allocate and intialize memory for RC4 AuxInfo structure.
//
if (!(pRC4AuxInfo = (CMSG_RC4_AUX_INFO *) ::CoTaskMemAlloc(sizeof(CMSG_RC4_AUX_INFO))))
{
hr = E_OUTOFMEMORY;
DebugTrace("Error: out of memory.\n");
goto ErrorExit;
}
::ZeroMemory(pRC4AuxInfo, sizeof(CMSG_RC4_AUX_INFO));
pRC4AuxInfo->cbSize = sizeof(CMSG_RC4_AUX_INFO);
//
// Determine key length requested.
//
if (CAPICOM_ENCRYPTION_KEY_LENGTH_MAXIMUM == KeyLength)
{
pRC4AuxInfo->dwBitLen = peex.dwMaxLen;
}
else if (CAPICOM_ENCRYPTION_KEY_LENGTH_40_BITS == KeyLength)
{
if (peex.dwMinLen <= 40 && 40 <= peex.dwMaxLen)
{
pRC4AuxInfo->dwBitLen = 40;
}
else
{
hr = CAPICOM_E_NOT_SUPPORTED;
DebugTrace("Error [%#x]: 40-bits encryption is not available.\n", hr);
goto ErrorExit;
}
}
else if (CAPICOM_ENCRYPTION_KEY_LENGTH_56_BITS == KeyLength)
{
if (peex.dwMinLen <= 56 && 56 <= peex.dwMaxLen)
{
pRC4AuxInfo->dwBitLen = 56;
}
else
{
hr = CAPICOM_E_NOT_SUPPORTED;
DebugTrace("Error [%#x]: 56-bits encryption is not available.\n", hr);
goto ErrorExit;
}
}
else if (CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS == KeyLength)
{
if (peex.dwMinLen <= 128 && 128 <= peex.dwMaxLen)
{
pRC4AuxInfo->dwBitLen = 128;
}
else
{
hr = CAPICOM_E_NOT_SUPPORTED;
DebugTrace("Error [%#x]: 128-bits encryption is not available.\n", hr);
goto ErrorExit;
}
}
else
{
//
// Should never get to here.
//
hr = CAPICOM_E_INTERNAL;
DebugTrace("Error [%#x]: Unknown key length (%d).\n", hr, KeyLength);
goto ErrorExit;
}
//
// Return RC4 AuxInfo pointer to caller.
//
*ppAuxInfo = (void *) pRC4AuxInfo;
}
CommonExit:
DebugTrace("Leaving SetKeyLength().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
//
// Free resource.
//
if (pRC2AuxInfo)
{
::CoTaskMemFree(pRC2AuxInfo);
}
if (pRC4AuxInfo)
{
::CoTaskMemFree(pRC4AuxInfo);
}
goto CommonExit;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : SetEncryptionAlgorithm
Synopsis : Setup the encryption algorithm structure.
Parameter: CAPICOM_ENCRYPTION_ALGORITHM AlgoName - Algorithm ID enum name.
CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength - Key length enum name.
CRYPT_ALGORITHM_IDENTIFIER * pEncryptAlgorithm - Pointer to the
structure.
Remark :
------------------------------------------------------------------------------*/
static HRESULT SetEncryptionAlgorithm (CAPICOM_ENCRYPTION_ALGORITHM AlgoName,
CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength,
CRYPT_ALGORITHM_IDENTIFIER * pEncryptAlgorithm)
{
HRESULT hr = S_OK;
ALG_ID AlgID = 0;
DebugTrace("Entering SetEncryptionAlgorithm().\n");
//
// Sanity check.
//
ATLASSERT(pEncryptAlgorithm);
//
// Initialize structure.
//
::ZeroMemory(pEncryptAlgorithm, sizeof(CRYPT_ALGORITHM_IDENTIFIER));
//
// Convert to LPSTR.
//
if (FAILED(hr = ::EnumNameToAlgID(AlgoName, KeyLength, &AlgID)))
{
DebugTrace("Error: EnumNameToAlgID() failed.\n");
goto ErrorExit;
}
if (FAILED(hr = ::AlgIDToOID(AlgID, &pEncryptAlgorithm->pszObjId)))
{
DebugTrace("Error: AlgIDToOID() failed.\n");
goto ErrorExit;
}
CommonExit:
DebugTrace("Leaving SetEncryptionAlgorithm().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
//
// Free resource.
//
if (pEncryptAlgorithm->pszObjId)
{
::CoTaskMemFree(pEncryptAlgorithm->pszObjId);
}
goto CommonExit;
}
////////////////////////////////////////////////////////////////////////////////
//
// CEnvelopedData
//
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : CEnvelopedData::get_Content
Synopsis : Return the content.
Parameter: BSTR * pVal - Pointer to BSTR to receive the content.
Remark :
------------------------------------------------------------------------------*/
STDMETHODIMP CEnvelopedData::get_Content (BSTR * pVal)
{
HRESULT hr = S_OK;
DebugTrace("Entering CEnvelopedData::get_Content().\n");
//
// Lock access to this object.
//
m_Lock.Lock();
try
{
//
// Check parameters.
//
if (NULL == pVal)
{
hr = E_INVALIDARG;
DebugTrace("Error [%#x]: Parameter pVal is NULL.\n", hr);
goto ErrorExit;
}
//
// Make sure content is already initialized.
//
if (0 == m_ContentBlob.cbData)
{
hr = CAPICOM_E_ENVELOP_NOT_INITIALIZED;
DebugTrace("Error [%#x]: Enveloped object has not been initialized.\n", hr);
goto ErrorExit;
}
//
// Sanity check.
//
ATLASSERT(m_ContentBlob.pbData);
//
// Return content.
//
if (FAILED(hr = ::BlobToBstr(&m_ContentBlob, pVal)))
{
DebugTrace("Error [%#x]: BlobToBstr() failed.\n", hr);
goto ErrorExit;
}
}
catch(...)
{
hr = E_POINTER;
DebugTrace("Exception: invalid parameter.\n");
goto ErrorExit;
}
UnlockExit:
//
// Unlock access to this object.
//
m_Lock.Unlock();
DebugTrace("Leaving CEnvelopedData::get_Content().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
ReportError(hr);
goto UnlockExit;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : CEnvelopedData::put_Content
Synopsis : Initialize the object with content to be enveloped.
Parameter: BSTR newVal - BSTR containing the content to be enveloped.
Remark :
------------------------------------------------------------------------------*/
STDMETHODIMP CEnvelopedData::put_Content (BSTR newVal)
{
HRESULT hr = S_OK;
DebugTrace("Entering CEnvelopedData::put_Content().\n");
//
// Lock access to this object.
//
m_Lock.Lock();
try
{
//
// Make sure parameters are valid.
//
if (0 == ::SysStringByteLen(newVal))
{
hr = E_INVALIDARG;
DebugTrace("Error [%#x]: Parameter newVal is NULL or empty.\n", hr);
goto ErrorExit;
}
//
// Update content.
//
if (FAILED(hr = ::BstrToBlob(newVal, &m_ContentBlob)))
{
DebugTrace("Error [%#x]: BstrToBlob() failed.\n", hr);
goto ErrorExit;
}
}
catch(...)
{
hr = E_POINTER;
DebugTrace("Exception: invalid parameter.\n");
goto ErrorExit;
}
UnlockExit:
//
// Unlock access to this object.
//
m_Lock.Unlock();
DebugTrace("Leaving CEnvelopedData::put_Content().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
ReportError(hr);
goto UnlockExit;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : CEnvelopedData::get_Algorithm
Synopsis : Property to return the algorithm object.
Parameter: IAlgorithm ** pVal - Pointer to pointer to IAlgorithm to receive
the interfcae pointer.
Remark :
------------------------------------------------------------------------------*/
STDMETHODIMP CEnvelopedData::get_Algorithm (IAlgorithm ** pVal)
{
HRESULT hr = S_OK;
DebugTrace("Entering CEnvelopedData::get_Algorithm().\n");
//
// Lock access to this object.
//
m_Lock.Lock();
try
{
//
// Check parameters.
//
if (NULL == pVal)
{
hr = E_INVALIDARG;
DebugTrace("Error [%#x]: Parameter pVal is NULL.\n", hr);
goto ErrorExit;
}
//
// Sanity check.
//
ATLASSERT(m_pIAlgorithm);
//
// Return interface pointer to caller.
//
if (FAILED(hr = m_pIAlgorithm->QueryInterface(pVal)))
{
DebugTrace("Unexpected error [%#x]: m_pIAlgorithm->QueryInterface() failed.\n", hr);
goto ErrorExit;
}
}
catch(...)
{
hr = E_POINTER;
DebugTrace("Exception: invalid parameter.\n");
goto ErrorExit;
}
UnlockExit:
//
// Unlock access to this object.
//
m_Lock.Unlock();
DebugTrace("Leaving CEnvelopedData::get_Algorithm().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
ReportError(hr);
goto UnlockExit;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : CEnvelopedData::get_Recipients
Synopsis : Property to return the IRecipients collection object.
Parameter: IRecipients ** pVal - Pointer to pointer to IRecipietns to receive
the interface pointer.
Remark :
------------------------------------------------------------------------------*/
STDMETHODIMP CEnvelopedData::get_Recipients (IRecipients ** pVal)
{
HRESULT hr = S_OK;
DebugTrace("Entering CEnvelopedData::get_Recipients().\n");
//
// Lock access to this object.
//
m_Lock.Lock();
try
{
//
// Check parameters.
//
if (NULL == pVal)
{
hr = E_INVALIDARG;
DebugTrace("Error [%#x]: Parameter pVal is NULL.\n", hr);
goto ErrorExit;
}
//
// Sanity check.
//
ATLASSERT(m_pIRecipients);
//
// Return interface pointer to caller.
//
if (FAILED(hr = m_pIRecipients->QueryInterface(pVal)))
{
DebugTrace("Unexpected error [%#x]: m_pIRecipients->QueryInterface() failed.\n", hr);
goto ErrorExit;
}
}
catch(...)
{
hr = E_POINTER;
DebugTrace("Exception: invalid parameter.\n");
goto ErrorExit;
}
UnlockExit:
//
// Unlock access to this object.
//
m_Lock.Unlock();
DebugTrace("Leaving CEnvelopedData::get_Recipients().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
ReportError(hr);
goto UnlockExit;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : CEnvelopedData::Encrypt
Synopsis : Envelop the content.
Parameter: CAPICOM_ENCODING_TYPE EncodingType - Encoding type.
BSTR * pVal - Pointer to BSTR to receive the enveloped message.
Remark :
------------------------------------------------------------------------------*/
STDMETHODIMP CEnvelopedData::Encrypt (CAPICOM_ENCODING_TYPE EncodingType,
BSTR * pVal)
{
HRESULT hr = S_OK;
HCRYPTMSG hMsg = NULL;
HCRYPTPROV hCryptProv = NULL;
CRYPT_DATA_BLOB MessageBlob = {0, NULL};
DebugTrace("Entering CEnvelopedData::Encrypt().\n");
//
// Lock access to this object.
//
m_Lock.Lock();
try
{
//
// Check parameters.
//
if (NULL == pVal)
{
hr = E_INVALIDARG;
DebugTrace("Error [%#x]: Parameter pVal is NULL.\n", hr);
goto ErrorExit;
}
//
// Make sure we do have content to envelop.
//
if (0 == m_ContentBlob.cbData)
{
hr = CAPICOM_E_ENVELOP_NOT_INITIALIZED;
DebugTrace("Error [%#x]: envelop object has not been initialized.\n", hr);
goto ErrorExit;
}
//
// Open a new message to encode.
//
if (FAILED(hr = OpenToEncode(&hMsg, &hCryptProv)))
{
DebugTrace("Error [%#x]: CEnvelopedData::OpenToEncode() failed.\n", hr);
goto ErrorExit;
}
//
// Update envelop content.
//
if(!::CryptMsgUpdate(hMsg,
m_ContentBlob.pbData,
m_ContentBlob.cbData,
TRUE))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptMsgUpdate() failed.\n", hr);
goto ErrorExit;
}
//
// Retrieve enveloped message.
//
if (FAILED(hr = ::GetMsgParam(hMsg,
CMSG_CONTENT_PARAM,
0,
(void **) &MessageBlob.pbData,
&MessageBlob.cbData)))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: GetMsgParam() failed to get message content.\n", hr);
goto ErrorExit;
}
//
// Now export the enveloped message.
//
if (FAILED(hr = ::ExportData(MessageBlob, EncodingType, pVal)))
{
DebugTrace("Error [%#x]: ExportData() failed.\n", hr);
goto ErrorExit;
}
//
// Write encoded blob to file, so we can use offline tool such as
// ASN parser to analyze message.
//
// The following line will resolve to void for non debug build, and
// thus can be safely removed if desired.
//
DumpToFile("Enveloped.asn", MessageBlob.pbData, MessageBlob.cbData);
}
catch(...)
{
hr = E_POINTER;
DebugTrace("Exception: invalid parameter.\n");
goto ErrorExit;
}
UnlockExit:
//
// Free resource.
//
if (MessageBlob.pbData)
{
::CoTaskMemFree(MessageBlob.pbData);
}
if (hCryptProv)
{
::ReleaseContext(hCryptProv);
}
if (hMsg)
{
::CryptMsgClose(hMsg);
}
//
// Unlock access to this object.
//
m_Lock.Unlock();
DebugTrace("Leaving CEnvelopedData::Encrypt().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
ReportError(hr);
goto UnlockExit;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : CEnvelopedData::Decrypt
Synopsis : Decrypt the enveloped message.
Parameter: BSTR EnvelopedMessage - BSTR containing the enveloped message.
Remark : If called from web environment, UI will be displayed, if has
not been prevously disabled, to warn the user of accessing the
private key for decrypting.
------------------------------------------------------------------------------*/
STDMETHODIMP CEnvelopedData::Decrypt (BSTR EnvelopedMessage)
{
HRESULT hr = S_OK;
HRESULT hr2 = S_OK;
HCERTSTORE hCertStores[2] = {NULL, NULL};
HCRYPTMSG hMsg = NULL;
PCCERT_CONTEXT pCertContext = NULL;
HCRYPTPROV hCryptProv = NULL;
DWORD dwKeySpec = 0;
BOOL bReleaseContext = FALSE;
BOOL bUserPrompted = FALSE;
DWORD dwNumRecipients = 0;
DWORD cbNumRecipients = sizeof(dwNumRecipients);
CRYPT_DATA_BLOB ContentBlob = {0, NULL};
DWORD dwIndex;
CComBSTR bstrContent;
DebugTrace("Entering CEnvelopedData::Decrypt().\n");
//
// Lock access to this object.
//
m_Lock.Lock();
try
{
//
// Reset member variables.
//
if (FAILED(hr = m_pIRecipients->Clear()))
{
DebugTrace("Error [%#x]: m_pIRecipients->Clear() failed.\n", hr);
goto ErrorExit;
}
//
// Make sure parameters are valid.
//
if (0 == ::SysStringByteLen(EnvelopedMessage))
{
hr = E_INVALIDARG;
DebugTrace("Error [%#x]: Parameter EnvelopedMessage is NULL or empty.\n", hr);
goto ErrorExit;
}
//
// Open current user and local machine MY stores.
//
hCertStores[0] = ::CertOpenStore(CERT_STORE_PROV_SYSTEM,
CAPICOM_ASN_ENCODING,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER | CERT_STORE_OPEN_EXISTING_FLAG,
L"My");
hCertStores[1] = ::CertOpenStore(CERT_STORE_PROV_SYSTEM,
CAPICOM_ASN_ENCODING,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE | CERT_STORE_OPEN_EXISTING_FLAG,
L"My");
//
// Did we manage to open any of the MY store?
//
if (NULL == hCertStores[0] && NULL == hCertStores[1])
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CertOpenStore() failed.\n", hr);
goto ErrorExit;
}
//
// Open the message for decode.
//
if (FAILED(hr = OpenToDecode(NULL, EnvelopedMessage, &hMsg)))
{
DebugTrace("Error [%#x]: CEnvelopedData::OpenToDecode() failed.\n", hr);
goto ErrorExit;
}
//
// Determine number of recipients.
//
if (!::CryptMsgGetParam(hMsg,
CMSG_RECIPIENT_COUNT_PARAM,
0,
(void *) &dwNumRecipients,
&cbNumRecipients))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptMsgGetParam() failed for CMSG_RECIPIENT_COUNT_PARAM.\n", hr);
goto ErrorExit;
}
//
// Find recipient.
//
for (dwIndex = 0; dwIndex < dwNumRecipients; dwIndex++)
{
BOOL bFound = FALSE;
DATA_BLOB CertInfoBlob = {0, NULL};
//
// Get RecipientInfo.
//
if (FAILED(hr = ::GetMsgParam(hMsg,
CMSG_RECIPIENT_INFO_PARAM,
dwIndex,
(void **) &CertInfoBlob.pbData,
&CertInfoBlob.cbData)))
{
DebugTrace("Error [%#x]: GetMsgParam() failed for CMSG_RECIPIENT_INFO_PARAM.\n", hr);
goto ErrorExit;
}
//
// Find recipient's cert in store.
//
if ((hCertStores[0] && (pCertContext = ::CertGetSubjectCertificateFromStore(hCertStores[0],
CAPICOM_ASN_ENCODING,
(CERT_INFO *) CertInfoBlob.pbData))) ||
(hCertStores[1] && (pCertContext = ::CertGetSubjectCertificateFromStore(hCertStores[1],
CAPICOM_ASN_ENCODING,
(CERT_INFO *) CertInfoBlob.pbData))))
{
bFound = TRUE;
}
//
// Free memory.
//
::CoTaskMemFree(CertInfoBlob.pbData);
//
// Did we find the recipient?
//
if (bFound)
{
CMSG_CTRL_DECRYPT_PARA DecryptPara;
//
// If we are called from a web page, we need to pop up UI
// to get user permission to perform decrypt operation.
//
if (!bUserPrompted)
{
if (m_dwCurrentSafety &&
FAILED(hr = OperationApproved(IDD_DECRYPT_SECURITY_ALERT_DLG)))
{
DebugTrace("Error [%#x]: OperationApproved() failed.\n", hr);
goto ErrorExit;
}
bUserPrompted = TRUE;
}
//
// Acquire CSP context.
//
if (S_OK == ::AcquireContext(pCertContext, &hCryptProv, &dwKeySpec, &bReleaseContext))
{
//
// Decypt the message.
//
::ZeroMemory(&DecryptPara, sizeof(DecryptPara));
DecryptPara.cbSize = sizeof(DecryptPara);
DecryptPara.hCryptProv = hCryptProv;
DecryptPara.dwKeySpec = dwKeySpec;
DecryptPara.dwRecipientIndex = dwIndex;
if(::CryptMsgControl(hMsg,
0,
CMSG_CTRL_DECRYPT,
&DecryptPara))
{
//
// Get decrypted content.
//
if (FAILED(hr = ::GetMsgParam(hMsg,
CMSG_CONTENT_PARAM,
0,
(void **) &ContentBlob.pbData,
&ContentBlob.cbData)))
{
DebugTrace("Error [%#x]: GetMsgParam() failed to get CMSG_CONTENT_PARAM.\n", hr);
goto ErrorExit;
}
//
// Update member variables.
//
m_ContentBlob = ContentBlob;
//
// We are all done, so break out of loop.
//
break;
}
else
{
//
// Keep copy of error code.
//
hr2 = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Info [%#x]: CryptMsgControl() failed to decrypt.\n", hr2);
}
if (bReleaseContext)
{
::ReleaseContext(hCryptProv), hCryptProv = NULL;
}
}
::CertFreeCertificateContext(pCertContext), pCertContext = NULL;
}
}
//
// Did we find the recipient?
//
if (dwIndex == dwNumRecipients)
{
//
// Retrieve previous error if any.
//
if (FAILED(hr2))
{
hr = hr2;
DebugTrace("Error [%#x]: CryptMsgControl() failed to decrypt.\n", hr);
}
else
{
hr = CAPICOM_E_ENVELOP_RECIPIENT_NOT_FOUND;
DebugTrace("Error [%#x]: recipient not found.\n", hr);
}
goto ErrorExit;
}
}
catch(...)
{
hr = E_POINTER;
DebugTrace("Exception: invalid parameter.\n");
goto ErrorExit;
}
UnlockExit:
//
// Free resource.
//
if (hCryptProv && bReleaseContext)
{
::ReleaseContext(hCryptProv);
}
if (pCertContext)
{
::CertFreeCertificateContext(pCertContext);
}
if(hMsg)
{
::CryptMsgClose(hMsg);
}
if (hCertStores[0])
{
::CertCloseStore(hCertStores[0], 0);
}
if (hCertStores[1])
{
::CertCloseStore(hCertStores[1], 0);
}
//
// Unlock access to this object.
//
m_Lock.Unlock();
DebugTrace("Leaving CEnvelopedData::Decrypt().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
//
// Free resources.
//
if (ContentBlob.pbData)
{
::CoTaskMemFree(ContentBlob.pbData);
}
ReportError(hr);
goto UnlockExit;
}
////////////////////////////////////////////////////////////////////////////////
//
// Private member functions.
//
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : CEnvelopedData::Init
Synopsis : Initialize the object.
Parameter: None.
Remark : This method is not part of the COM interface (it is a normal C++
member function). We need it to initialize the object created
internally by us.
Since it is only a normal C++ member function, this function can
only be called from a C++ class pointer, not an interface pointer.
------------------------------------------------------------------------------*/
STDMETHODIMP CEnvelopedData::Init ()
{
HRESULT hr = S_OK;
CComPtr<IAlgorithm> pIAlgorithm = NULL;
CComPtr<IRecipients> pIRecipients = NULL;
DebugTrace("Entering CEnvelopedData::Init().\n");
//
// Create embeded IAlgorithm.
//
if (FAILED(hr = ::CreateAlgorithmObject(FALSE, FALSE, &pIAlgorithm)))
{
DebugTrace("Error [%#x]: CreateAlgorithmObject() failed.\n", hr);
goto CommonExit;
}
//
// Create embeded IRecipients.
//
if (FAILED(hr = ::CreateRecipientsObject(&pIRecipients)))
{
DebugTrace("Error [%#x]: CreateRecipientsObject() failed.\n", hr);
goto CommonExit;
}
//
// Update member variables.
//
m_bEnveloped = FALSE;
m_ContentBlob.cbData = 0;
m_ContentBlob.pbData = NULL;
m_pIAlgorithm = pIAlgorithm;
m_pIRecipients = pIRecipients;
CommonExit:
DebugTrace("Leaving CEnvelopedData::Init().\n");
return hr;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : CEnvelopedData::OpenToEncode
Synopsis : Create and initialize an envelop message for encoding.
Parameter: HCRYPTMSG * phMsg - Pointer to HCRYPTMSG to receive message
handler.
HCRYPTPROV * phCryptProv - Pointer to HCRYPTPROV to receive CSP
handler.
Remark :
------------------------------------------------------------------------------*/
STDMETHODIMP CEnvelopedData::OpenToEncode (HCRYPTMSG * phMsg,
HCRYPTPROV * phCryptProv)
{
HRESULT hr = S_OK;
DWORD dwNumRecipients = 0;
PCCERT_CONTEXT * pCertContexts = NULL;
PCERT_INFO * pCertInfos = NULL;
HCRYPTPROV hCryptProv = NULL;
void * pEncryptionAuxInfo = NULL;
CAPICOM_STORE_INFO StoreInfo = {0, L"AddressBook"};
CComPtr<ICertificate> pIRecipient = NULL;
CComPtr<ICertificate2> pICertificate2 = NULL;
DWORD dwIndex;
CAPICOM_ENCRYPTION_ALGORITHM AlgoName;
CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength;
CMSG_ENVELOPED_ENCODE_INFO EnvelopInfo;
CRYPT_ALGORITHM_IDENTIFIER EncryptionAlgorithm;
DebugTrace("Entering CEnvelopedData::OpenToEncode().\n");
//
// Sanity check.
//
ATLASSERT(phMsg);
ATLASSERT(phCryptProv);
ATLASSERT(m_ContentBlob.cbData && m_ContentBlob.pbData);
ATLASSERT(m_pIRecipients);
//
// Initialize.
//
::ZeroMemory(&EnvelopInfo, sizeof(EnvelopInfo));
::ZeroMemory(&EncryptionAlgorithm, sizeof(EncryptionAlgorithm));
//
// Make sure we do have at least 1 recipient.
//
if (FAILED(hr = m_pIRecipients->get_Count((long *) &dwNumRecipients)))
{
DebugTrace("Error [%#x]: m_pIRecipients->get_Count() failed.\n", hr);
goto ErrorExit;
}
if (0 == dwNumRecipients)
{
//
// Prompt user to add a recipient.
//
if (FAILED(hr = ::SelectCertificate(StoreInfo,
SelectRecipientCertCallback,
&pICertificate2)))
{
if (hr == CAPICOM_E_STORE_EMPTY)
{
hr = CAPICOM_E_ENVELOP_NO_RECIPIENT;
}
DebugTrace("Error [%#x]: SelectCertificate() failed.\n", hr);
goto ErrorExit;
}
if (FAILED(hr = pICertificate2->QueryInterface(__uuidof(ICertificate), (void **) &pIRecipient)))
{
DebugTrace("Unexpected error [%#x]: pICertificate2->QueryInterface() failed.\n", hr);
goto ErrorExit;
}
//
// Add to collection.
//
if (FAILED (hr = m_pIRecipients->Add(pIRecipient)))
{
DebugTrace("Error [%#x]: m_pIRecipients->Add() failed.\n", hr);
goto ErrorExit;
}
//
// Make sure count is 1.
//
if (FAILED(hr = m_pIRecipients->get_Count((long *) &dwNumRecipients)))
{
DebugTrace("Error [%#x]: m_pIRecipients->get_Count() failed.\n", hr);
goto ErrorExit;
}
//
// Sanity check.
//
ATLASSERT(1 == dwNumRecipients);
}
//
// Allocate memory for CERT_CONTEXT array.
//
if (!(pCertContexts = (PCCERT_CONTEXT *) ::CoTaskMemAlloc(sizeof(PCCERT_CONTEXT) * dwNumRecipients)))
{
hr = E_OUTOFMEMORY;
DebugTrace("Error: out of memory.\n");
goto ErrorExit;
}
::ZeroMemory(pCertContexts, sizeof(PCCERT_CONTEXT) * dwNumRecipients);
//
// Allocate memory for CERT_INFO array.
//
if (!(pCertInfos = (PCERT_INFO *) ::CoTaskMemAlloc(sizeof(PCERT_INFO) * dwNumRecipients)))
{
hr = E_OUTOFMEMORY;
DebugTrace("Error: out of memory.\n");
goto ErrorExit;
}
::ZeroMemory(pCertInfos, sizeof(PCERT_INFO) * dwNumRecipients);
//
// Set CERT_INFO array.
//
for (dwIndex = 0; dwIndex < dwNumRecipients; dwIndex++)
{
CComVariant varRecipient;
CComPtr<ICertificate> pIRecipient2 = NULL;
//
// Get next recipient.
//
if (FAILED(hr = m_pIRecipients->get_Item((long) (dwIndex + 1),
&varRecipient)))
{
DebugTrace("Error [%#x]: m_pIRecipients->get_Item() failed.\n", hr);
goto ErrorExit;
}
//
// Get custom interface.
//
if (FAILED(hr = varRecipient.pdispVal->QueryInterface(IID_ICertificate,
(void **) &pIRecipient2)))
{
DebugTrace("Error [%#x]: varRecipient.pdispVal->QueryInterface() failed.\n", hr);
goto ErrorExit;
}
//
// Get CERT_CONTEXT.
//
if (FAILED(hr = ::GetCertContext(pIRecipient2, &pCertContexts[dwIndex])))
{
DebugTrace("Error [%#x]: GetCertContext() failed.\n", hr);
goto ErrorExit;
}
//
// Set CERT_INFO.
//
pCertInfos[dwIndex] = pCertContexts[dwIndex]->pCertInfo;
}
//
// Get algorithm ID enum name.
//
if (FAILED(hr = m_pIAlgorithm->get_Name(&AlgoName)))
{
DebugTrace("Error [%#x]: m_pIAlgorithm->get_Name() failed.\n", hr);
goto ErrorExit;
}
//
// Get key length enum name.
//
if (FAILED(hr = m_pIAlgorithm->get_KeyLength(&KeyLength)))
{
DebugTrace("Error [%#x]: m_pIAlgorithm->get_KeyLength() failed.\n", hr);
goto ErrorExit;
}
//
// Get CSP context.
//
if (FAILED(hr = ::AcquireContext(AlgoName,
KeyLength,
&hCryptProv)))
{
DebugTrace("Error [%#x]: AcquireContext() failed.\n", hr);
goto ErrorExit;
}
//
// Set algorithm.
//
if (FAILED(hr = ::SetEncryptionAlgorithm(AlgoName,
KeyLength,
&EncryptionAlgorithm)))
{
DebugTrace("Error [%#x]: SetEncryptionAlgorithm() failed.\n", hr);
goto ErrorExit;
}
//
// Set key length.
//
if (FAILED(hr = ::SetKeyLength(hCryptProv,
EncryptionAlgorithm,
KeyLength,
&pEncryptionAuxInfo)))
{
DebugTrace("Error [%#x]: SetKeyLength() failed.\n", hr);
goto ErrorExit;
}
//
// Set CMSG_ENVELOPED_ENCODE_INFO.
//
EnvelopInfo.cbSize = sizeof(EnvelopInfo);
EnvelopInfo.ContentEncryptionAlgorithm = EncryptionAlgorithm;
EnvelopInfo.hCryptProv = hCryptProv;
EnvelopInfo.cRecipients = dwNumRecipients;
EnvelopInfo.rgpRecipients = pCertInfos;
EnvelopInfo.pvEncryptionAuxInfo = pEncryptionAuxInfo;
//
// Open the message for encoding.
//
if(!(*phMsg = ::CryptMsgOpenToEncode(CAPICOM_ASN_ENCODING, // ASN encoding type
0, // Flags
CMSG_ENVELOPED, // Message type
&EnvelopInfo, // Pointer to structure
NULL, // Inner content OID
NULL))) // Stream information (not used)
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptMsgOpenToEncode() failed.\n", hr);
goto ErrorExit;
}
//
// Return HCRYPTPROV to caller.
//
*phCryptProv = hCryptProv;
CommonExit:
//
// Free resource.
//
if (pEncryptionAuxInfo)
{
::CoTaskMemFree(pEncryptionAuxInfo);
}
if (EncryptionAlgorithm.pszObjId)
{
::CoTaskMemFree(EncryptionAlgorithm.pszObjId);
}
if (EncryptionAlgorithm.Parameters.pbData)
{
::CoTaskMemFree(EncryptionAlgorithm.Parameters.pbData);
}
if (pCertInfos)
{
::CoTaskMemFree(pCertInfos);
}
if (pCertContexts)
{
for (dwIndex = 0; dwIndex < dwNumRecipients; dwIndex++)
{
if (pCertContexts[dwIndex])
{
::CertFreeCertificateContext(pCertContexts[dwIndex]);
}
}
::CoTaskMemFree(pCertContexts);
}
DebugTrace("Leaving CEnvelopedData::OpenToEncode().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
//
// Free resource.
//
if (hCryptProv)
{
::ReleaseContext(hCryptProv);
}
goto CommonExit;
}
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function : CEnvelopedData::OpenToDeccode
Synopsis : Open an enveloped message for decoding.
Parameter: HCRYPTPROV hCryptProv - CSP handle.
BSTR EnvelopMessage - Enveloped message.
HCRYPTMSG * phMsg - Pointer to HCRYPTMSG.
Remark :
------------------------------------------------------------------------------*/
STDMETHODIMP CEnvelopedData::OpenToDecode (HCRYPTPROV hCryptProv,
BSTR EnvelopedMessage,
HCRYPTMSG * phMsg)
{
HRESULT hr = S_OK;
HCRYPTMSG hMsg = NULL;
DWORD dwMsgType = 0;
DWORD cbMsgType = sizeof(dwMsgType);
DATA_BLOB MessageBlob = {0, NULL};
DATA_BLOB AlgorithmBlob = {0, NULL};
DATA_BLOB ParametersBlob = {0, NULL};
DebugTrace("Leaving CEnvelopedData::OpenToDecode().\n");
// Sanity check.
//
ATLASSERT(phMsg);
ATLASSERT(EnvelopedMessage);
try
{
//
// Open the message for decoding.
//
if (!(hMsg = ::CryptMsgOpenToDecode(CAPICOM_ASN_ENCODING,
0,
0,
hCryptProv,
NULL,
NULL)))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptMsgOpenToDecode() failed.\n", hr);
goto CommonExit;
}
//
// Import the message.
//
if (FAILED(hr = ::ImportData(EnvelopedMessage, CAPICOM_ENCODE_ANY, &MessageBlob)))
{
DebugTrace("Error [%#x]: ImportData() failed.\n", hr);
goto ErrorExit;
}
//
// Update message with enveloped content.
//
if (!::CryptMsgUpdate(hMsg,
MessageBlob.pbData,
MessageBlob.cbData,
TRUE))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptMsgUpdate() failed.\n", hr);
goto ErrorExit;
}
//
// Check message type.
//
if (!::CryptMsgGetParam(hMsg,
CMSG_TYPE_PARAM,
0,
(void *) &dwMsgType,
&cbMsgType))
{
hr = HRESULT_FROM_WIN32(::GetLastError());
DebugTrace("Error [%#x]: CryptMsgGetParam() failed for CMSG_TYPE_PARAM.\n", hr);
goto ErrorExit;
}
if (CMSG_ENVELOPED != dwMsgType)
{
hr = CAPICOM_E_ENVELOP_INVALID_TYPE;
DebugTrace("Error [%#x]: Enveloped message's dwMsgType (%#x) is not CMSG_ENVELOPED.\n", hr, dwMsgType);
goto ErrorExit;
}
//
// Get algorithm ID.
//
if (FAILED(hr = ::GetMsgParam(hMsg,
CMSG_ENVELOPE_ALGORITHM_PARAM,
0,
(void **) &AlgorithmBlob.pbData,
&AlgorithmBlob.cbData)))
{
DebugTrace("Error [%#x]: GetMsgParam() failed for CMSG_ENVELOPE_ALGORITHM_PARAM.\n", hr);
goto ErrorExit;
}
//
// Restore encryption algorithm state, as much as we can.
//
if (0 == lstrcmpA(szOID_RSA_RC2CBC, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->pszObjId))
{
CAPICOM_ENCRYPTION_KEY_LENGTH KeyLength;
DebugTrace("INFO: Envelop encryption algorithm was RC2.\n");
//
// Set encryption algorithm name.
//
if (FAILED(hr = m_pIAlgorithm->put_Name(CAPICOM_ENCRYPTION_ALGORITHM_RC2)))
{
DebugTrace("Error [%#x]: m_pIAlgorithm->put_Name() failed.\n", hr);
goto ErrorExit;
}
//
// Determine encryption key length.
//
if (((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->Parameters.cbData)
{
if (FAILED(hr = ::DecodeObject(PKCS_RC2_CBC_PARAMETERS,
((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->Parameters.pbData,
((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->Parameters.cbData,
&ParametersBlob)))
{
DebugTrace("Error [%#x]: DecodeObject() failed for PKCS_RC2_CBC_PARAMETERS.\n", hr);
goto ErrorExit;
}
}
//
// Set encryption key length.
//
switch (((CRYPT_RC2_CBC_PARAMETERS *) ParametersBlob.pbData)->dwVersion)
{
case CRYPT_RC2_40BIT_VERSION:
{
KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_40_BITS;
DebugTrace("INFO: Envelop encryption key length was 40-bits.\n");
break;
}
case CRYPT_RC2_56BIT_VERSION:
{
KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_56_BITS;
DebugTrace("INFO: Envelop encryption key length was 56-bits.\n");
break;
}
case CRYPT_RC2_128BIT_VERSION:
{
KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_128_BITS;
DebugTrace("INFO: Envelop encryption key length was 128-bits.\n");
break;
}
default:
{
//
// Unknown key length, so arbitrary choose one.
//
KeyLength = CAPICOM_ENCRYPTION_KEY_LENGTH_MAXIMUM;
DebugTrace("INFO: Unknown envelop encryption key length.\n");
break;
}
}
//
// Set key length.
//
if (FAILED(hr = m_pIAlgorithm->put_KeyLength(KeyLength)))
{
DebugTrace("Error [%#x]: m_pIAlgorithm->put_KeyLength() failed.\n", hr);
goto ErrorExit;
}
}
else if (0 == lstrcmpA(szOID_RSA_RC4, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->pszObjId))
{
DebugTrace("INFO: Envelop encryption algorithm was RC4.\n");
//
// Set encryption algorithm name.
//
if (FAILED(hr = m_pIAlgorithm->put_Name(CAPICOM_ENCRYPTION_ALGORITHM_RC4)))
{
DebugTrace("Error [%#x]: m_pIAlgorithm->put_Name() failed.\n", hr);
goto ErrorExit;
}
//
// For RC4, CAPI simply does not provide a way to retrieve the
// encryption key length, so we will have to leave it alone!!!
//
}
else if (0 == lstrcmpA(szOID_OIWSEC_desCBC, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->pszObjId))
{
DebugTrace("INFO: Envelop encryption algorithm was DES.\n");
//
// Set encryption algorithm name.
//
if (FAILED(hr = m_pIAlgorithm->put_Name(CAPICOM_ENCRYPTION_ALGORITHM_DES)))
{
DebugTrace("Error [%#x]: m_pIAlgorithm->put_Name() failed.\n", hr);
goto ErrorExit;
}
//
// For DES, key length is fixed at 56, and should be ignored.
//
}
else if (0 == lstrcmpA(szOID_RSA_DES_EDE3_CBC, ((CRYPT_ALGORITHM_IDENTIFIER *) AlgorithmBlob.pbData)->pszObjId))
{
DebugTrace("INFO: Envelop encryption algorithm was 3DES.\n");
//
// Set encryption algorithm name.
//
if (FAILED(hr = m_pIAlgorithm->put_Name(CAPICOM_ENCRYPTION_ALGORITHM_3DES)))
{
DebugTrace("Error [%#x]: m_pIAlgorithm->put_Name() failed.\n", hr);
goto ErrorExit;
}
//
// For 3DES, key length is fixed at 168, and should be ignored.
//
}
else
{
DebugTrace("INFO: Unknown envelop encryption algorithm.\n");
}
//
// Return msg handler to caller.
//
*phMsg = hMsg;
}
catch(...)
{
hr = E_POINTER;
DebugTrace("Exception: invalid parameter.\n");
goto ErrorExit;
}
CommonExit:
//
// Free resource.
//
if (ParametersBlob.pbData)
{
::CoTaskMemFree(ParametersBlob.pbData);
}
if (AlgorithmBlob.pbData)
{
::CoTaskMemFree(AlgorithmBlob.pbData);
}
if (MessageBlob.pbData)
{
::CoTaskMemFree(MessageBlob.pbData);
}
DebugTrace("Leaving CEnvelopedData::OpenToDecode().\n");
return hr;
ErrorExit:
//
// Sanity check.
//
ATLASSERT(FAILED(hr));
if (hMsg)
{
::CryptMsgClose(hMsg);
}
goto CommonExit;
}