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.
3910 lines
132 KiB
3910 lines
132 KiB
/*
|
|
** capistm.cpp
|
|
**
|
|
** Purpose:
|
|
** Implementation of a class to wrap around CAPI functionality
|
|
**
|
|
** History
|
|
** 1/26/98; (brucek) triple wrap support
|
|
** 2/07/97: (t-erikne) multipart/signed
|
|
** 1/06/97: (t-erikne) Moved into MimeOLE
|
|
** 11/14/96: (t-erikne) CAPI Post-SDR work
|
|
** 8/27/96: (t-erikne) Created.
|
|
**
|
|
** Copyright (C) Microsoft Corp. 1996-1998.
|
|
*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Depends on
|
|
//
|
|
|
|
#include "pch.hxx"
|
|
#include <wincrypt.h>
|
|
#include "olealloc.h"
|
|
#include "containx.h"
|
|
#include "smime.h"
|
|
#include "capistm.h"
|
|
#include "mimeapi.h"
|
|
#include "inetconv.h"
|
|
#include <capiutil.h>
|
|
#ifndef MAC
|
|
#include <shlwapi.h>
|
|
#endif // !MAC
|
|
#include <demand.h>
|
|
#include "strconst.h"
|
|
|
|
#include "smimepol.h"
|
|
BOOL FHideMsgWithDifferentLabels();
|
|
enum ECertErrorProcessLabel {
|
|
CertErrorProcessLabelAnyway = 0,
|
|
CertErrorProcessLabelGrant = 1,
|
|
CertErrorProcessLabelDeny = 2
|
|
};
|
|
DWORD DwProcessLabelWithCertError();
|
|
HRESULT HrCheckLabelAccess(const DWORD dwFlags, const HWND hwnd,
|
|
PSMIME_SECURITY_LABEL plabel, const PCCERT_CONTEXT pccertDecrypt,
|
|
const PCCERT_CONTEXT pccertSigner, const HCERTSTORE hcertstor);
|
|
|
|
#ifdef MAC
|
|
#undef CertOpenStore
|
|
EXTERN_C WINCRYPT32API HCERTSTORE WINAPI MacCertOpenStore(LPCSTR lpszStoreProvider,
|
|
DWORD dwEncodingType,
|
|
HCRYPTPROV hCryptProv,
|
|
DWORD dwFlags,
|
|
const void *pvPara);
|
|
#define CertOpenStore MacCertOpenStore
|
|
#endif // MAC
|
|
|
|
// from dllmain.h
|
|
extern DWORD g_dwSysPageSize;
|
|
extern CMimeAllocator * g_pMoleAlloc;
|
|
extern ULONG DllAddRef(void);
|
|
extern ULONG DllRelease(void);
|
|
|
|
extern void DebugDumpStreamToFile(LPSTREAM pstm, LPSTR lpszFile);
|
|
|
|
// From smime.cpp
|
|
extern HRESULT HrGetLastError(void);
|
|
extern BOOL FIsMsasn1Loaded();
|
|
|
|
#ifdef WIN16
|
|
#define CRYPT_ACQUIRE_CONTEXT CryptAcquireContextA
|
|
#else
|
|
#define CRYPT_ACQUIRE_CONTEXT CryptAcquireContextW
|
|
#endif
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// defines
|
|
//
|
|
|
|
#define THIS_AS_UNK ((IUnknown *)(IStream *)this)
|
|
|
|
#define CS_E_CANT_DECRYPT MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, 0x2414)
|
|
#define CS_E_MSG_INVALID MAKE_SCODE(SEVERITY_ERROR, FACILITY_ITF, 0x2415)
|
|
|
|
const int CbCacheBufferSize = 4 * 1024;
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// inlines
|
|
//
|
|
|
|
static INLINE void ReleaseCert(PCCERT_CONTEXT pc)
|
|
{ if (pc) CertFreeCertificateContext(pc); }
|
|
|
|
// Streaming state diagram
|
|
//
|
|
// SF GT SD-FW-TN-SO-SF
|
|
// | | /[encryption]
|
|
// SO QT-------------------
|
|
// [op encode] \ / [opaque decode] \[signed]
|
|
// ------NB------ (FW-TN)-SO-SF
|
|
// [det encode] / \ [detached decode]
|
|
// DO DO
|
|
// | \
|
|
// SF DF
|
|
// |
|
|
// SO
|
|
// |
|
|
// SF
|
|
//
|
|
//
|
|
// New state diagram:
|
|
// encrypt
|
|
// SD--SO
|
|
// opaque encode opaque decode /
|
|
// SO QT--------------------[QTF]---
|
|
// \ / \
|
|
// ---NB----- SO signed
|
|
// / \
|
|
// SO---DO DO ------------------ SO
|
|
// detach encode detach decode
|
|
//
|
|
//
|
|
|
|
#ifndef WIN16
|
|
enum CSstate {
|
|
STREAM_NOT_BEGUN,
|
|
STREAM_QUESTION_TIME,
|
|
STREAM_QUESTION_TIME_FINAL,
|
|
STREAM_SETUP_DECRYPT,
|
|
STREAM_DETACHED_OCCURING,
|
|
STREAM_OCCURING, // must be +1 of DF
|
|
STREAM_ERROR,
|
|
STREAM_GOTTYPE,
|
|
|
|
CSTM_FIRST_WRITE = 32,
|
|
CSTM_TEST_NESTING,
|
|
CSTM_STREAMING,
|
|
CSTM_STREAMING_DONE,
|
|
CSTM_GOTTYPE,
|
|
};
|
|
#endif // !WIN16
|
|
|
|
// low word is public. see .h file.
|
|
#define CSTM_DECODE 0x00010000
|
|
#define CSTM_DONTRELEASEPROV 0x00020000
|
|
#define CSTM_RECURSED 0x00040000
|
|
#define CSTM_HAVECR 0x10000000
|
|
#define CSTM_HAVEEOL 0x20000000
|
|
|
|
static const char s_cszMy[] = "My";
|
|
static const char s_cszWABCertStore[] = "AddressBook";
|
|
static const char s_cszCA[] = "CA";
|
|
|
|
static const char s_cszMimeHeader[] = "Content-Type: application/x-pkcs7-mime"
|
|
"; name=smime.p7m; smime-type=";
|
|
static const char s_cszMimeHeader2[] = "Content-Disposition: attachment; "
|
|
"filename=smime.p7m";
|
|
|
|
static const char s_cszOIDMimeHeader1[] = "Content-Type: oid/";
|
|
static const char s_cszOIDMimeHeader2[] = "\nContent-Transfer-Encoding: binary\n\n";
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// static prototypes
|
|
//
|
|
|
|
#if 0
|
|
#define IV_LENGTH 8
|
|
static BOOL _GetIV(BYTE rgbIV[IV_LENGTH]);
|
|
|
|
static PBYTE _PVEncodeObject(
|
|
LPCSTR lpszStructType,
|
|
const void *pvStructInfo,
|
|
DWORD *pcbEncoded);
|
|
#endif
|
|
|
|
static HRESULT _InitEncodedCert(IN HCERTSTORE hcertstor,
|
|
OUT PCERT_BLOB * rgblobCert, OUT DWORD * pcCerts,
|
|
OUT PCRL_BLOB * rgblobCRL, OUT DWORD * pcCrl);
|
|
|
|
static HRESULT _InitEncodedCertIncludingSigners(IN HCERTSTORE hcertstor,
|
|
DWORD cSigners, SignerData rgSigners[],
|
|
PCERT_BLOB * prgblobCerts, DWORD * pcCerts,
|
|
PCRL_BLOB * prgblobCrls, DWORD * pcCrl);
|
|
|
|
static void _SMimeCapsFromHMsg(HCRYPTMSG, DWORD id, LPBYTE * ppb, DWORD * pcb);
|
|
|
|
// ---------------------------- UTILITY FUNCTIONS --------------------------
|
|
|
|
|
|
HRESULT GetParameters(PCCERT_CONTEXT pccert, HCERTSTORE hstoreMsg,
|
|
HCERTSTORE hstoreAll)
|
|
{
|
|
CRYPT_DATA_BLOB blob;
|
|
DWORD dw;
|
|
HRESULT hr = CRYPT_E_MISSING_PUBKEY_PARA;
|
|
PCCERT_CONTEXT pccertX;
|
|
|
|
//
|
|
// Start by looking for the issuer certificate on your own. All that
|
|
// matters is that we find a certificate which claims to be the issuer
|
|
// and has parameters -- they will need to verify the parameters are
|
|
// correct at a later date.
|
|
//
|
|
|
|
pccertX = NULL;
|
|
while (hstoreMsg != NULL) {
|
|
//
|
|
// Find certificates by matching issuers -- ok for now as PKIX requires
|
|
// all issuers to have DNs
|
|
//
|
|
|
|
dw = CERT_STORE_SIGNATURE_FLAG;
|
|
pccertX = CertGetIssuerCertificateFromStore(hstoreMsg, pccert, pccertX,
|
|
&dw);
|
|
if (pccertX == NULL) {
|
|
if (::GetLastError() == CRYPT_E_SELF_SIGNED) {
|
|
return S_OK;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Only accept the item if we manage a signature validation on it.
|
|
//
|
|
|
|
if ((dw & CERT_STORE_SIGNATURE_FLAG)) {
|
|
//
|
|
// We can't verify the signature, so get the issuers paramters and try again
|
|
//
|
|
|
|
hr = GetParameters(pccertX, hstoreMsg, hstoreAll);
|
|
if (FAILED(hr)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The issuing cert has parameters, try the signature check again againist it.
|
|
//
|
|
|
|
dw = CERT_STORE_SIGNATURE_FLAG;
|
|
if (CertVerifySubjectCertificateContext(pccert, pccertX, &dw) && (dw == 0)) {
|
|
break;
|
|
}
|
|
hr = CRYPT_E_MISSING_PUBKEY_PARA;
|
|
}
|
|
else {
|
|
if (pccertX->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.cbData != 0) {
|
|
hr = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we found one but it does not have the parameters, it must be
|
|
// inheriting from it's issuer as well.
|
|
//
|
|
|
|
dw = CERT_STORE_SIGNATURE_FLAG;
|
|
if (CertVerifySubjectCertificateContext(pccert, pccertX, &dw) && (dw == 0)) {
|
|
hr = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we still do not have a certificate, then search all of the system stores
|
|
// for an isuer.
|
|
//
|
|
|
|
if (pccertX == NULL) {
|
|
while (hstoreAll != NULL) {
|
|
//
|
|
// Find certificates by matching issuers -- ok for now as PKIX requires
|
|
// all issuers to have DNs
|
|
//
|
|
|
|
dw = CERT_STORE_SIGNATURE_FLAG;
|
|
pccertX = CertGetIssuerCertificateFromStore(hstoreAll, pccert, pccertX,
|
|
&dw);
|
|
|
|
if (pccertX == NULL) {
|
|
if (::GetLastError() == CRYPT_E_SELF_SIGNED) {
|
|
return S_OK;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Only accept the item if we manage a signature validation on it.
|
|
//
|
|
|
|
if ((dw & CERT_STORE_SIGNATURE_FLAG)) {
|
|
//
|
|
// We can't verify the signature, so get the issuers paramters and try again
|
|
//
|
|
|
|
hr = GetParameters(pccertX, hstoreMsg, hstoreAll);
|
|
if (FAILED(hr)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The issuing cert has parameters, try the signature check again againist it.
|
|
//
|
|
|
|
dw = CERT_STORE_SIGNATURE_FLAG;
|
|
if (CertVerifySubjectCertificateContext(pccert, pccertX, &dw) && (dw == 0)) {
|
|
break;
|
|
}
|
|
hr = CRYPT_E_MISSING_PUBKEY_PARA;
|
|
}
|
|
else {
|
|
if (pccertX->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.cbData != 0) {
|
|
hr = 0;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we found one but it does not have the parameters, it must be inheriting
|
|
// from it's issuer as well.
|
|
//
|
|
|
|
dw = CERT_STORE_SIGNATURE_FLAG;
|
|
if (CertVerifySubjectCertificateContext(pccert, pccertX, &dw) && (dw == 0)) {
|
|
hr = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// We found a certificate, set the parameters onto the context so that we
|
|
// can successfully manage to validate the signature
|
|
//
|
|
|
|
if (pccertX != NULL) {
|
|
CRYPT_DATA_BLOB * pdata = NULL;
|
|
|
|
hr = HrGetCertificateParam(pccert, CERT_PUBKEY_ALG_PARA_PROP_ID, (LPVOID *) &pdata, NULL);
|
|
if (FAILED(hr)) {
|
|
CertFreeCertificateContext(pccertX);
|
|
return hr;
|
|
}
|
|
|
|
CertSetCertificateContextProperty(pccert, CERT_PUBKEY_ALG_PARA_PROP_ID, 0, pdata);
|
|
|
|
ReleaseMem(pdata);
|
|
CertFreeCertificateContext(pccertX);
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// if we still have not found anything, then let the caller have a chance
|
|
// to tell us what the parameters ought to be.
|
|
//
|
|
|
|
if (m_pSmimeCallback != NULL) {
|
|
hr = m_pSmimeCallback->GetParameters(pSignerCert, NULL,
|
|
&blob.cbData, &blob.pbData);
|
|
if (SUCCEEDED(hr)) {
|
|
if (!CertSetCertificateContextProperty(pccert, CERT_PUBKEY_ALG_PARA_PROP_ID, 0, &blob) {
|
|
hr = HrGetLastError();
|
|
}
|
|
}
|
|
if (pb != NULL) {
|
|
LocalFree(blob.pbData);
|
|
}
|
|
if (SUCCEEDED(hr)) {
|
|
goto retry;
|
|
}
|
|
}
|
|
#endif // 0
|
|
|
|
return hr;
|
|
}
|
|
|
|
//*************************************************************************
|
|
// CCAPIStm
|
|
//*************************************************************************
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ctor/dtor
|
|
//
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : constructor
|
|
|
|
Purpose :
|
|
|
|
Parameters: lpstmOut -> Output stream or NULL
|
|
psld -> SECURITY_LAYER_DATA or NULL. If NULL, one will be
|
|
created.
|
|
|
|
Returns : void
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
CCAPIStm::CCAPIStm(LPSTREAM lpstmOut) :
|
|
m_pstmOut(lpstmOut), m_cRef(1)
|
|
{
|
|
DOUT("CCAPIStm::constructor() %#x -> %d", this, m_cRef);
|
|
if (m_pstmOut)
|
|
m_pstmOut->AddRef();
|
|
|
|
m_hProv = NULL;
|
|
m_hMsg = NULL;
|
|
// m_buffer = NULL;
|
|
m_csStatus = STREAM_NOT_BEGUN;
|
|
m_csStream = CSTM_FIRST_WRITE;
|
|
m_rgStores = NULL;
|
|
m_cStores = 0;
|
|
m_pUserCertDecrypt = NULL;
|
|
m_pCapiInner = NULL;
|
|
m_pConverter = NULL;
|
|
m_psldData = NULL;
|
|
m_pattrAuth = NULL;
|
|
|
|
#if defined(DEBUG) && !defined(MAC)
|
|
{
|
|
char szFileName[MAX_PATH + 1];
|
|
|
|
m_pstmDebugFile = NULL;
|
|
// Create a debug output file name based on the CAPIStm pointer
|
|
wnsprintfA(szFileName, ARRAYSIZE(szFileName), "c:\\capidump%08x.txt", this);
|
|
OpenFileStream(szFileName, CREATE_ALWAYS, GENERIC_WRITE, &m_pstmDebugFile);
|
|
}
|
|
#endif
|
|
|
|
m_hwnd = NULL;
|
|
m_pSmimeCallback = NULL;
|
|
m_dwFlagsSEF = 0;
|
|
m_pwszKeyPrompt = NULL;
|
|
|
|
m_pbBuffer = NULL;
|
|
m_cbBuffer = 0;
|
|
|
|
// m_dwFlags set in HrInitialize
|
|
// m_cbBeginWrite initialized before use
|
|
// m_cbBufUsed handled in the Begin* functions
|
|
// m_cbBufAlloc handled in the Begin* functions
|
|
}
|
|
|
|
CCAPIStm::~CCAPIStm()
|
|
{
|
|
DOUT("CCAPIStm::destructor() %#x -> %d", this, m_cRef);
|
|
if (m_hMsg) {
|
|
CryptMsgClose(m_hMsg);
|
|
}
|
|
|
|
if (m_hProv)
|
|
{
|
|
CryptReleaseContext(m_hProv, 0);
|
|
}
|
|
|
|
m_hProv = NULL;
|
|
ReleaseObj(m_pCapiInner);
|
|
ReleaseObj(m_pstmOut);
|
|
ReleaseObj(m_pConverter);
|
|
if (m_pattrAuth) {
|
|
MemFree(m_pattrAuth);
|
|
}
|
|
if (m_pUserCertDecrypt) {
|
|
CertFreeCertificateContext(m_pUserCertDecrypt);
|
|
}
|
|
if (m_cStores) {
|
|
Assert(m_rgStores);
|
|
for (DWORD i=0; i<m_cStores; i++) {
|
|
CertCloseStore(m_rgStores[i], 0);
|
|
}
|
|
MemFree(m_rgStores);
|
|
}
|
|
|
|
// Fix: Releasing hProv is caller responcibility
|
|
//if (m_hProv && !(m_dwFlagsStm & CSTM_DONTRELEASEPROV)) {
|
|
// CryptReleaseContext(m_hProv, 0);
|
|
//}
|
|
|
|
#if defined(DEBUG) && !defined(MAC)
|
|
SafeRelease(m_pstmDebugFile);
|
|
#endif
|
|
|
|
if (m_psldData) {
|
|
m_psldData->Release();
|
|
}
|
|
|
|
if (m_pbBuffer != NULL) {
|
|
MemFree(m_pbBuffer);
|
|
}
|
|
|
|
SafeMemFree(m_pwszKeyPrompt);
|
|
|
|
ReleaseObj(m_pSmimeCallback);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IUnknown methods
|
|
//
|
|
|
|
STDMETHODIMP CCAPIStm::QueryInterface(REFIID riid, LPVOID *ppv)
|
|
{
|
|
if (!ppv) {
|
|
return TrapError(E_INVALIDARG);
|
|
}
|
|
|
|
// Find IID
|
|
if (IID_IUnknown == riid) {
|
|
*ppv = THIS_AS_UNK;
|
|
}
|
|
else if (IID_IStream == riid) {
|
|
*ppv = (IStream *)this;
|
|
}
|
|
else {
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
((IUnknown *)*ppv)->AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CCAPIStm::AddRef(void)
|
|
{
|
|
DOUT("CCAPIStm::AddRef() %#x -> %d", this, m_cRef+1);
|
|
InterlockedIncrement((LPLONG)&m_cRef);
|
|
return m_cRef;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CCAPIStm::Release(void)
|
|
{
|
|
DOUT("CCAPIStm::Release() %#x -> %d", this, m_cRef-1);
|
|
if (0 == InterlockedDecrement((LPLONG)&m_cRef)) {
|
|
delete this;
|
|
return 0;
|
|
}
|
|
return m_cRef;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IStream methods
|
|
//
|
|
|
|
STDMETHODIMP CCAPIStm::Seek(LARGE_INTEGER, DWORD, ULARGE_INTEGER *plibNewPosition)
|
|
{
|
|
if (!plibNewPosition) {
|
|
return E_POINTER;
|
|
}
|
|
else {
|
|
plibNewPosition->HighPart = 0;
|
|
plibNewPosition->LowPart = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//// CCAPIStm::Write
|
|
//
|
|
// Description:
|
|
// This function is called with the original message as the buffer
|
|
// being written into this stream object. We then make the appropriate
|
|
// calls into the NT Crypto system in order to encrypt/decrypt the message.
|
|
//
|
|
// Part of what this function needs to do is to interact with the
|
|
// Crypto system in order to cause decryption of message to occur.
|
|
|
|
#ifndef WIN16
|
|
STDMETHODIMP CCAPIStm::Write(const void *pv, ULONG cb, ULONG *pcbActual)
|
|
#else
|
|
STDMETHODIMP CCAPIStm::Write(const void HUGEP *pv, ULONG cb, ULONG *pcbActual)
|
|
#endif // !WIN16
|
|
{
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Reset the return arg just incase
|
|
//
|
|
|
|
if (pcbActual != NULL) {
|
|
*pcbActual = 0;
|
|
}
|
|
|
|
//
|
|
// If the CMS object is not still open, then we are dead and need to return an error.
|
|
//
|
|
|
|
if (!m_hMsg) {
|
|
hr = CAPISTM_E_MSG_CLOSED;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Are we in the correct state to take anything.
|
|
//
|
|
|
|
switch (m_csStatus) {
|
|
case STREAM_NOT_BEGUN:
|
|
Assert(FALSE); // Should never get here
|
|
hr = CAPISTM_E_NOT_BEGUN;
|
|
goto exit;
|
|
case STREAM_DETACHED_OCCURING:
|
|
case STREAM_QUESTION_TIME:
|
|
case STREAM_SETUP_DECRYPT:
|
|
case STREAM_OCCURING:
|
|
break;
|
|
case STREAM_ERROR:
|
|
Assert(FALSE); // Should never get here
|
|
hr = CAPISTM_E_OVERDONE;
|
|
goto exit;
|
|
case STREAM_GOTTYPE:
|
|
hr = CAPISTM_E_GOTTYPE;
|
|
goto exit;
|
|
|
|
// We should go from QT to QTF in this function, and never come back
|
|
// until we have changed the state again.
|
|
default:
|
|
Assert(FALSE);
|
|
case STREAM_QUESTION_TIME_FINAL:
|
|
hr = E_UNEXPECTED;
|
|
goto exit;
|
|
}
|
|
|
|
#if defined(DEBUG) && !defined(MAC)
|
|
//
|
|
// Flush the input buffer to disk so that we can debug it later if necessary
|
|
//
|
|
|
|
if (!m_pCapiInner && m_pstmDebugFile) {
|
|
m_pstmDebugFile->Write((BYTE *)pv, cb, NULL);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// We need to start buffering data to make our messages shorter. The output
|
|
// from the save code comes in one and two byte chucks often, we need to put
|
|
// the data out in larger blocks
|
|
//
|
|
|
|
if (m_pbBuffer != NULL) {
|
|
//
|
|
// If we would overflow the buffer, then dump the cached buffer out
|
|
//
|
|
|
|
if (m_cbBuffer + cb > CbCacheBufferSize) {
|
|
if (!CryptMsgUpdate(m_hMsg, m_pbBuffer, m_cbBuffer, FALSE)) {
|
|
// CryptMsgUpdate failed
|
|
|
|
Assert(S_OK != HrGetLastError());
|
|
hr = HrGetLastError();
|
|
if (FAILED(hr)) {
|
|
m_csStatus = STREAM_ERROR;
|
|
}
|
|
goto exit;
|
|
}
|
|
m_cbBuffer = 0;
|
|
}
|
|
|
|
//
|
|
// If this buffer will over flow, then dump out just that item. Otherwise
|
|
// we are just going to cache the buffer.
|
|
//
|
|
|
|
if (cb >= CbCacheBufferSize) {
|
|
if (!CryptMsgUpdate(m_hMsg, (BYTE *) pv, cb, FALSE)) {
|
|
// CryptMsgUpdate failed
|
|
|
|
Assert(S_OK != HrGetLastError());
|
|
hr = HrGetLastError();
|
|
if (FAILED(hr)) {
|
|
m_csStatus = STREAM_ERROR;
|
|
}
|
|
goto exit;
|
|
}
|
|
}
|
|
else {
|
|
memcpy(m_pbBuffer + m_cbBuffer, pv, cb);
|
|
m_cbBuffer += cb;
|
|
}
|
|
|
|
if (pcbActual != NULL) {
|
|
*pcbActual = cb;
|
|
}
|
|
|
|
//
|
|
// The only time we should be here is when we are creating a new CMS object
|
|
// and thus all of the code below this is not relavent as we don't ever
|
|
// need to ask questions about what type of this message.
|
|
//
|
|
|
|
hr = S_OK;
|
|
goto exit;
|
|
}
|
|
else {
|
|
//
|
|
// Push the input buffer into the Crypto system. On failures from the
|
|
// system we need to propigate the correct error state into our structure
|
|
// and into the return value.
|
|
//
|
|
|
|
if (!CryptMsgUpdate(m_hMsg, (BYTE *)pv, cb, FALSE)) {
|
|
// CryptMsgUpdate failed
|
|
|
|
Assert(S_OK != HrGetLastError());
|
|
hr = HrGetLastError();
|
|
if (FAILED(hr)) {
|
|
m_csStatus = STREAM_ERROR;
|
|
}
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Since the CryptMsgUpdate call succeeded, return
|
|
// a nice out param (specifically that we have consumed all of the passed
|
|
// in bytes)
|
|
//
|
|
|
|
if (pcbActual) {
|
|
*pcbActual = cb;
|
|
}
|
|
hr = S_OK;
|
|
|
|
//
|
|
// If we are in a state where we need to ask questions about the message,
|
|
// then proceed to do so.
|
|
//
|
|
|
|
if ((STREAM_QUESTION_TIME == m_csStatus) ||
|
|
(STREAM_QUESTION_TIME_FINAL == m_csStatus)) {
|
|
|
|
DWORD cbDWORD, dwMsgType;
|
|
|
|
// We should never be asking questions if encoding.
|
|
Assert(m_dwFlagsStm & CSTM_DECODE);
|
|
|
|
//
|
|
// Find out what security services have been placed onto this
|
|
// message object (if any). If not enough bytes have been processed
|
|
// to find out what the encoding of the message is, then return
|
|
// success so we can get more bytes and get the question answered
|
|
// at a later date.
|
|
//
|
|
|
|
cbDWORD = sizeof(DWORD);
|
|
if (!CryptMsgGetParam(m_hMsg, CMSG_TYPE_PARAM, 0, &dwMsgType, &cbDWORD)) {
|
|
hr = HrGetLastError();
|
|
Assert (S_OK != hr);
|
|
if (CRYPT_E_STREAM_MSG_NOT_READY == hr) {
|
|
hr = S_OK;
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
// Since we are here, we must have a V1 type S/MIME message
|
|
Assert(m_psldData);
|
|
m_psldData->m_dwMsgEnhancement = MST_CLASS_SMIME_V1;
|
|
hr = S_OK;
|
|
|
|
//
|
|
// Set the correct flags based on the message type of the object we are
|
|
// decoding.
|
|
//
|
|
|
|
switch (dwMsgType) {
|
|
case CMSG_ENVELOPED:
|
|
m_psldData->m_dwMsgEnhancement |= MST_THIS_ENCRYPT;
|
|
break;
|
|
case CMSG_SIGNED:
|
|
m_psldData->m_dwMsgEnhancement |= MST_THIS_BLOBSIGN;
|
|
break;
|
|
case CMSG_SIGNED_AND_ENVELOPED:
|
|
m_psldData->m_dwMsgEnhancement |= MST_THIS_BLOBSIGN | MST_THIS_ENCRYPT;
|
|
break;
|
|
|
|
default:
|
|
// K this is a little rude. not my iface error.
|
|
hr = MIME_E_SECURITY_BADSECURETYPE;
|
|
|
|
// just return the CAPI type if we don't recognize
|
|
m_psldData->m_dwMsgEnhancement = dwMsgType;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If all we are asking for is a type and we don't have any other errors,
|
|
// mark the fact that we got the type and return that fact as the
|
|
// error (to prevent futher buffers being written into us.)
|
|
//
|
|
|
|
if (CSTM_TYPE_ONLY & m_dwFlagsStm) {
|
|
CSSDOUT("Got Type on typeonly call.");
|
|
CSSDOUT("You will now see 80041417 failures; they're okay.");
|
|
m_csStatus = STREAM_GOTTYPE;
|
|
if (SUCCEEDED(hr)) {
|
|
hr = CAPISTM_E_GOTTYPE;
|
|
}
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Change the object state based on the message type. If we need to
|
|
// setup a decryption, then we need to mark the state for that.
|
|
// If we are just signing, then we can just let the rest of the
|
|
// streaming occur.
|
|
//
|
|
|
|
if (CMSG_ENVELOPED == dwMsgType) {
|
|
m_csStatus = STREAM_SETUP_DECRYPT;
|
|
}
|
|
else {
|
|
m_csStatus = STREAM_OCCURING;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we need to set-up the message for decryption, then do so at this
|
|
// point.
|
|
//
|
|
|
|
Assert(SUCCEEDED(hr));
|
|
if (STREAM_SETUP_DECRYPT == m_csStatus) {
|
|
// Can't decrypt detached messages
|
|
Assert(!(m_dwFlagsStm & CSTM_DETACHED));
|
|
|
|
// We are now streaming the data out, on the assumption that the
|
|
// decryption stats.
|
|
m_csStatus = STREAM_OCCURING;
|
|
hr = HandleEnveloped();
|
|
|
|
// If we failed to decrypt, then re-map some errors and change the
|
|
// state back in the event that not all of the lock boxes have
|
|
// been seen yet.
|
|
if (FAILED(hr)) {
|
|
if (CRYPT_E_STREAM_MSG_NOT_READY == hr) {
|
|
m_csStatus = STREAM_SETUP_DECRYPT;
|
|
hr = S_OK;
|
|
}
|
|
else if (CS_E_CANT_DECRYPT == hr) {
|
|
hr = MIME_E_SECURITY_CANTDECRYPT;
|
|
// m_csStatus = STREAM_FINAL; // M00QUEST
|
|
}
|
|
else {
|
|
if (CS_E_MSG_INVALID == hr) {
|
|
hr = MIME_E_SECURITY_CANTDECRYPT;
|
|
}
|
|
m_csStatus = STREAM_ERROR;
|
|
}
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
exit:
|
|
#ifdef DEBUG
|
|
if (CAPISTM_E_GOTTYPE != hr) {
|
|
return TrapError(hr);
|
|
}
|
|
else {
|
|
return hr; // don't spew this
|
|
}
|
|
#else
|
|
return hr;
|
|
#endif
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CCAPIStm public methods
|
|
//
|
|
|
|
|
|
|
|
|
|
/* HrInnerInitialize:
|
|
**
|
|
** Purpose:
|
|
** the standard "my constructor can't return errors" function
|
|
** Takes:
|
|
** dwFlagsSEF - Control Flags
|
|
** hwndParent - modal UI parents to this
|
|
** dwFlagsStm - see capistm.h
|
|
** Returns:
|
|
** OLE_E_INVALIDHWND if you give me a bad window
|
|
** MIME_E_SECURITY_NOOP if MST_NONE is the current psi type
|
|
** Notes:
|
|
** dwFlags is currently 0 for encode. do it this way.
|
|
*/
|
|
HRESULT CCAPIStm::HrInnerInitialize(DWORD dwFlagsSEF, const HWND hwndParent,
|
|
DWORD dwFlagsStm, IMimeSecurityCallback * pCallback,
|
|
PSECURITY_LAYER_DATA psld)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Save the security layer data
|
|
//
|
|
|
|
if (psld)
|
|
{
|
|
psld->AddRef();
|
|
m_psldData = psld;
|
|
}
|
|
else
|
|
{
|
|
IF_NULLEXIT(m_psldData = new(SECURITY_LAYER_DATA));
|
|
}
|
|
|
|
//
|
|
// Save the flags
|
|
//
|
|
|
|
m_dwFlagsSEF = dwFlagsSEF;
|
|
m_dwFlagsStm = dwFlagsStm;
|
|
|
|
if (pCallback != NULL)
|
|
{
|
|
m_pSmimeCallback = pCallback;
|
|
pCallback->AddRef();
|
|
}
|
|
|
|
//
|
|
// Make sure that if we have a window, it is a real window.
|
|
//
|
|
|
|
IF_TRUEEXIT((hwndParent && !IsWindow(hwndParent)), OLE_E_INVALIDHWND);
|
|
|
|
//
|
|
// Shove the hwnd into any crypto provider we openned up.
|
|
//
|
|
|
|
CryptSetProvParam(NULL, PP_CLIENT_HWND, (BYTE *)&hwndParent, 0);
|
|
m_hwnd = hwndParent;
|
|
|
|
exit:
|
|
return hr;
|
|
|
|
}
|
|
|
|
/* HrInitialize:
|
|
**
|
|
** Purpose:
|
|
** the standard "my constructor can't return errors" function
|
|
** Takes:
|
|
** dwFlagsSEF - Control Flags
|
|
** hwndParent - modal UI parents to this
|
|
** fEncode - trivial
|
|
** psi - message state information. see smime.h
|
|
** dwFlagsStm - see capistm.h
|
|
** Returns:
|
|
** OLE_E_INVALIDHWND if you give me a bad window
|
|
** MIME_E_SECURITY_NOOP if MST_NONE is the current psi type
|
|
** Notes:
|
|
** dwFlags is currently 0 for encode. do it this way.
|
|
*/
|
|
HRESULT CCAPIStm::HrInitialize(DWORD dwFlagsSEF, const HWND hwndParent,
|
|
const BOOL fEncode, SMIMEINFO *const psi,
|
|
DWORD dwFlagsStm, IMimeSecurityCallback * pCallback,
|
|
PSECURITY_LAYER_DATA psld)
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
// do the initialization common to all capi stream objects.
|
|
CHECKHR(hr = HrInnerInitialize(dwFlagsSEF, hwndParent, dwFlagsStm, pCallback, psld));
|
|
|
|
|
|
if (fEncode) {
|
|
hr = BeginEncodeStreaming(psi);
|
|
}
|
|
else {
|
|
hr = BeginDecodeStreaming(psi);
|
|
}
|
|
|
|
exit:
|
|
return TrapError(hr);
|
|
}
|
|
|
|
|
|
|
|
/* EndStreaming:
|
|
**
|
|
** Purpose:
|
|
** Push CAPI's message state forward a notch
|
|
** Returns:
|
|
** HRESULT
|
|
*/
|
|
HRESULT CCAPIStm::EndStreaming()
|
|
{
|
|
DWORD dwMsgEnhancement = m_psldData->m_dwMsgEnhancement;
|
|
HRESULT hr = S_OK;
|
|
PCMSG_ATTR pUnprotectedAttrs = NULL;
|
|
|
|
Assert(m_hMsg);
|
|
|
|
|
|
// If we are crurent in an error state then return
|
|
if ((STREAM_ERROR == m_csStatus) || STREAM_GOTTYPE == m_csStatus) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// If we are decoding -- and we are doing a detached message we need
|
|
// to jump from the sign object to the real body here.
|
|
//
|
|
|
|
if ((CSTM_DECODE & m_dwFlagsStm) && (STREAM_DETACHED_OCCURING == m_csStatus)) {
|
|
Assert(m_csStream == CSTM_STREAMING_DONE);
|
|
|
|
// client has finished giving us the signature block
|
|
m_csStatus = STREAM_OCCURING;
|
|
m_csStream = CSTM_STREAMING;
|
|
|
|
m_psldData->m_dwMsgEnhancement = MST_THIS_SIGN;
|
|
|
|
CSSDOUT("Signature streaming finished.");
|
|
if (! CryptMsgUpdate(m_hMsg, m_pbBuffer, m_cbBuffer, TRUE)) {
|
|
if ((hr = HrGetLastError()) == 0x80070000) { // CAPI sometimes doesn't SetLastError
|
|
hr = 0x80070000 | ERROR_ACCESS_DENIED;
|
|
}
|
|
}
|
|
m_cbBuffer = 0;
|
|
goto exit;
|
|
}
|
|
|
|
if (! CryptMsgUpdate(m_hMsg, m_pbBuffer, m_cbBuffer, TRUE)) {
|
|
if ((hr = HrGetLastError()) == 0x80070000) { // CAPI sometimes doesn't SetLastError
|
|
hr = 0x80070000 | ERROR_ACCESS_DENIED;
|
|
}
|
|
goto exit;
|
|
}
|
|
m_cbBuffer = 0;
|
|
|
|
if (m_dwFlagsStm & CSTM_DETACHED) {
|
|
m_csStatus = STREAM_OCCURING;
|
|
}
|
|
|
|
//
|
|
// do final streaming and verification
|
|
//
|
|
|
|
if (CSTM_DECODE & m_dwFlagsStm) {
|
|
if (MST_THIS_SIGN & dwMsgEnhancement) {
|
|
hr = VerifySignedMessage();
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
} else {
|
|
Assert(STREAM_OCCURING == m_csStatus);
|
|
Assert(CSTM_STREAMING_DONE == m_csStream);
|
|
if (g_FSupportV3 && (MST_THIS_ENCRYPT & dwMsgEnhancement)) {
|
|
BOOL f;
|
|
DWORD cbData = 0;
|
|
LPBYTE pb = NULL;
|
|
|
|
f = CryptMsgGetParam(m_hMsg, CMSG_UNPROTECTED_ATTR_PARAM, 0, NULL, &cbData);
|
|
if (!f) {
|
|
// Probably, message doesn't have a CMSG_UNPROTECTED_ATTR_PARAM
|
|
hr = HrGetLastError();
|
|
if(hr != CRYPT_E_ATTRIBUTES_MISSING)
|
|
goto exit;
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
cbData = 0;
|
|
}
|
|
}
|
|
if (cbData != 0) {
|
|
if (!MemAlloc((LPVOID *) &pUnprotectedAttrs, cbData)) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
f = CryptMsgGetParam(m_hMsg, CMSG_UNPROTECTED_ATTR_PARAM, 0, pUnprotectedAttrs, &cbData);
|
|
Assert(f);
|
|
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, szOID_Microsoft_Attribute_Sequence,
|
|
pUnprotectedAttrs, CRYPT_ENCODE_ALLOC_FLAG,
|
|
&CryptEncodeAlloc, &pb, &cbData)) {
|
|
hr = HrGetLastError();
|
|
goto exit;
|
|
}
|
|
m_psldData->m_blobUnprotectAttrs.cbData = cbData;
|
|
m_psldData->m_blobUnprotectAttrs.pbData = pb;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// fill in some more of the data structure
|
|
//
|
|
|
|
if ((CSTM_DECODE & m_dwFlagsStm) &&
|
|
(dwMsgEnhancement & MST_THIS_ENCRYPT)) {
|
|
_SMimeCapsFromHMsg(m_hMsg, CMSG_ENVELOPE_ALGORITHM_PARAM,
|
|
&m_psldData->m_blobDecAlg.pBlobData,
|
|
&m_psldData->m_blobDecAlg.cbSize);
|
|
}
|
|
|
|
if (m_pCapiInner) {
|
|
hr = m_pCapiInner->EndStreaming();
|
|
}
|
|
exit:
|
|
SafeMemFree(pUnprotectedAttrs);
|
|
if (hr == ERROR_ACCESS_DENIED) {
|
|
hr = E_ACCESSDENIED; // convert CAPI error to OLE HRESULT
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
PSECURITY_LAYER_DATA CCAPIStm::GetSecurityLayerData() const
|
|
{
|
|
if (m_psldData) {
|
|
m_psldData->AddRef();
|
|
}
|
|
return(m_psldData);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Implementation methods
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Encode / Decode
|
|
//
|
|
|
|
|
|
HRESULT CCAPIStm::BeginEncodeStreaming(SMIMEINFO *const psi)
|
|
{
|
|
DWORD cb;
|
|
DWORD cbData;
|
|
DWORD cCrls = 0;
|
|
DWORD cCerts = 0;
|
|
DWORD cSigners = 0;
|
|
HRESULT hr;
|
|
DWORD i;
|
|
DWORD dwMsgType;
|
|
DWORD dwFlags = 0;
|
|
PCRYPT_KEY_PROV_INFO pKPI;
|
|
CMSG_STREAM_INFO cmsi;
|
|
DWORD dwPsiType;
|
|
DWORD iSigner;
|
|
PCRYPT_ATTRIBUTES pattrsUnprot = NULL;
|
|
PCRYPT_ATTRIBUTES * rgpattrAuth = NULL;
|
|
PCRYPT_ATTRIBUTES * rgpattrUnauth = NULL;
|
|
#ifndef SMIME_V3
|
|
PCRYPT_SMIME_CAPABILITIES pcaps = NULL;
|
|
#endif // SMIME_V3
|
|
CMSG_RC2_AUX_INFO rc2Aux;
|
|
CRL_BLOB* rgCrlBlob;
|
|
PCRYPT_SMIME_CAPABILITIES * rgpcaps = NULL;
|
|
CMSG_SIGNER_ENCODE_INFO * rgSigner;
|
|
// #ifndef _WIN64
|
|
union {
|
|
struct {
|
|
// #endif
|
|
// anything that comes first must be common (in size)
|
|
// to both structures
|
|
CERT_INFO** rgpCertInfo;
|
|
CMSG_ENVELOPED_ENCODE_INFO ceei;
|
|
// #ifndef _WIN64
|
|
};
|
|
struct {
|
|
// #endif
|
|
CERT_BLOB* rgCertBlob;
|
|
CMSG_SIGNED_ENCODE_INFO csei;
|
|
// #ifndef _WIN64
|
|
};
|
|
};
|
|
// #endif
|
|
|
|
////////////
|
|
// can only return from here down
|
|
|
|
m_csStatus = STREAM_ERROR;
|
|
rgSigner = NULL;
|
|
|
|
if (!psi) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
//
|
|
// Get the security operations to be performed on this body layer.
|
|
// we only care about the current body properties so mask out
|
|
// other layers.
|
|
// If we don't have any security to perform, then get out of here
|
|
//
|
|
|
|
dwPsiType = m_psldData->m_dwMsgEnhancement & MST_THIS_MASK;
|
|
|
|
if (MST_NONE == dwPsiType) {
|
|
AssertSz(dwPsiType != MST_NONE, "Why are we here if we have no security to apply?");
|
|
return TrapError(MIME_E_SECURITY_NOOP);
|
|
}
|
|
|
|
//
|
|
// detached is the only allowed user settable flag
|
|
//
|
|
|
|
if ((m_dwFlagsStm & CSTM_ALLFLAGS) & ~CSTM_DETACHED) {
|
|
return TrapError(E_INVALIDARG);
|
|
}
|
|
|
|
rgCertBlob = NULL;
|
|
rgCrlBlob = NULL;
|
|
pKPI = NULL;
|
|
|
|
////////////
|
|
// can goto end from here down
|
|
|
|
//
|
|
// We should never be in a situation where we are going to both encrypt and sign
|
|
// a message.
|
|
//
|
|
|
|
AssertSz((!!(dwPsiType & MST_THIS_SIGN) +
|
|
!!(dwPsiType & MST_THIS_ENCRYPT)) == 1,
|
|
"Encrypt and Sign Same Layer is not legal");
|
|
|
|
if (dwPsiType & MST_THIS_SIGN) {
|
|
dwMsgType = CMSG_SIGNED;
|
|
|
|
if (!(m_psldData->m_dwMsgEnhancement & MST_BLOB_FLAG)) {
|
|
dwFlags |= CMSG_DETACHED_FLAG;
|
|
}
|
|
|
|
cSigners = m_psldData->m_cSigners;
|
|
|
|
if (m_psldData->m_hcertstor != NULL) {
|
|
hr = _InitEncodedCertIncludingSigners(m_psldData->m_hcertstor,
|
|
cSigners, m_psldData->m_rgSigners,
|
|
&rgCertBlob, &cCerts,
|
|
&rgCrlBlob, &cCrls);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
cb = sizeof(CMSG_SIGNER_ENCODE_INFO) * cSigners;
|
|
if (!MemAlloc((LPVOID *) &rgSigner, cb)) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
memset(rgSigner, 0, cb);
|
|
|
|
cb = sizeof(PCRYPT_SMIME_CAPABILITIES) * cSigners;
|
|
if (!MemAlloc((LPVOID *) &rgpcaps, cb)) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
memset(rgpcaps, 0, cb);
|
|
|
|
if (!MemAlloc((LPVOID *) &rgpattrAuth, cSigners*sizeof(PCRYPT_ATTRIBUTES))) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
memset(rgpattrAuth, 0, cSigners*sizeof(PCRYPT_ATTRIBUTES));
|
|
|
|
if (!MemAlloc((LPVOID *) &rgpattrUnauth, cSigners*sizeof(PCRYPT_ATTRIBUTES))) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
memset(rgpattrUnauth, 0, cSigners*sizeof(PCRYPT_ATTRIBUTES));
|
|
|
|
for (iSigner=0; iSigner<cSigners; iSigner++) {
|
|
rgSigner[iSigner].cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
|
|
rgSigner[iSigner].pvHashAuxInfo = NULL;
|
|
|
|
// We need to pull apart the algorithm used to sign the message so
|
|
// we can pass it down to the crypt32 code
|
|
|
|
hr = HrDecodeObject(m_psldData->m_rgSigners[iSigner].blobHashAlg.pBlobData,
|
|
m_psldData->m_rgSigners[iSigner].blobHashAlg.cbSize,
|
|
PKCS_SMIME_CAPABILITIES, 0, &cbData,
|
|
(LPVOID *)&rgpcaps[iSigner]);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
// MOOBUG -- MEMORY LEAK ON PCAPS!!!!
|
|
|
|
Assert(rgpcaps[iSigner] != NULL);
|
|
Assert(rgpcaps[iSigner]->cCapability == 1);
|
|
rgSigner[iSigner].HashAlgorithm.pszObjId = rgpcaps[iSigner]->rgCapability[0].pszObjId;
|
|
rgSigner[iSigner].HashAlgorithm.Parameters.cbData = rgpcaps[iSigner]->rgCapability[0].Parameters.cbData;
|
|
rgSigner[iSigner].HashAlgorithm.Parameters.pbData = rgpcaps[iSigner]->rgCapability[0].Parameters.pbData;
|
|
|
|
//
|
|
// Need to setup the attributes to attach to the signed message
|
|
//
|
|
|
|
if (m_psldData->m_rgSigners[iSigner].blobAuth.cbSize != 0) {
|
|
cbData = 0;
|
|
hr = HrDecodeObject(m_psldData->m_rgSigners[iSigner].blobAuth.pBlobData,
|
|
m_psldData->m_rgSigners[iSigner].blobAuth.cbSize,
|
|
szOID_Microsoft_Attribute_Sequence, 0,
|
|
&cbData, (LPVOID *)&rgpattrAuth[iSigner]);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
if (rgpattrAuth[iSigner] != NULL) {
|
|
rgSigner[iSigner].cAuthAttr = rgpattrAuth[iSigner]->cAttr;
|
|
rgSigner[iSigner].rgAuthAttr = rgpattrAuth[iSigner]->rgAttr;
|
|
Assert(m_pattrAuth == NULL);
|
|
if (!g_FSupportV3) {
|
|
// This code exists for old versions of crypt32. Prior to
|
|
// the NT5 re-write the capi code did not copy the attributes
|
|
// but assumed that we must have done so.
|
|
m_pattrAuth = rgpattrAuth[iSigner];
|
|
rgpattrAuth[iSigner] = NULL;
|
|
}
|
|
} else {
|
|
Assert(rgSigner[iSigner].cAuthAttr == 0);
|
|
Assert(rgSigner[iSigner].rgAuthAttr == NULL);
|
|
}
|
|
}
|
|
|
|
if (m_psldData->m_rgSigners[iSigner].blobUnauth.cbSize != 0) {
|
|
cbData = 0;
|
|
HrDecodeObject(m_psldData->m_rgSigners[iSigner].blobUnauth.pBlobData,
|
|
m_psldData->m_rgSigners[iSigner].blobUnauth.cbSize,
|
|
szOID_Microsoft_Attribute_Sequence, 0,
|
|
&cbData, (LPVOID *)&rgpattrUnauth[iSigner]);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
if (rgpattrUnauth[iSigner] != NULL) {
|
|
rgSigner[iSigner].cUnauthAttr = rgpattrUnauth[iSigner]->cAttr;
|
|
rgSigner[iSigner].rgUnauthAttr = rgpattrUnauth[iSigner]->rgAttr;
|
|
} else {
|
|
Assert(rgSigner[iSigner].cUnauthAttr == 0);
|
|
Assert(rgSigner[iSigner].rgUnauthAttr == NULL);
|
|
}
|
|
}
|
|
|
|
// load the provider information from the signing cert and then
|
|
// acquire that provider with the appropriate key container
|
|
|
|
hr = HrGetCertificateParam(m_psldData->m_rgSigners[iSigner].pccert,
|
|
CERT_KEY_PROV_INFO_PROP_ID,
|
|
(LPVOID *) &pKPI, NULL);
|
|
if (FAILED(hr)) {
|
|
goto gle;
|
|
}
|
|
|
|
if (!m_hProv && ! CRYPT_ACQUIRE_CONTEXT(&m_hProv, pKPI->pwszContainerName,
|
|
pKPI->pwszProvName, pKPI->dwProvType,
|
|
pKPI->dwFlags)) {
|
|
goto gle;
|
|
}
|
|
Assert(0 == pKPI->cProvParam);
|
|
|
|
#ifdef SMIME_V3
|
|
if (psi->pwszKeyPrompt != NULL) {
|
|
CryptSetProvParam(m_hProv, PP_UI_PROMPT, (LPBYTE) psi->pwszKeyPrompt, 0);
|
|
}
|
|
#endif // SMIME_V3
|
|
|
|
rgSigner[iSigner].pCertInfo = m_psldData->m_rgSigners[iSigner].pccert->pCertInfo;
|
|
rgSigner[iSigner].hCryptProv = m_hProv;
|
|
rgSigner[iSigner].dwKeySpec = pKPI->dwKeySpec;
|
|
|
|
//
|
|
// Need to change dsa to dsa-with-sha1
|
|
//
|
|
|
|
if (strcmp(rgSigner[iSigner].pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, szOID_OIWSEC_dsa) == 0) {
|
|
rgSigner[iSigner].HashEncryptionAlgorithm.pszObjId = szOID_OIWSEC_dsaSHA1;
|
|
}
|
|
}
|
|
|
|
csei.cbSize = sizeof(CMSG_SIGNED_ENCODE_INFO);
|
|
csei.cSigners = m_psldData->m_cSigners;
|
|
csei.rgSigners = rgSigner;
|
|
csei.cCertEncoded = cCerts;
|
|
csei.rgCertEncoded = rgCertBlob;
|
|
csei.cCrlEncoded = cCrls;
|
|
csei.rgCrlEncoded = rgCrlBlob;
|
|
}
|
|
|
|
//
|
|
// If it is not signed, then it must be encrypted. Setup the calls for
|
|
// performing an encryption operation.
|
|
//
|
|
else {
|
|
Assert((dwPsiType & MST_THIS_ENCRYPT) != 0);
|
|
|
|
dwMsgType = CMSG_ENVELOPED;
|
|
|
|
//
|
|
// If we are given a CSP, then we are going to pass it on to the Crypt32 code,
|
|
// However it turns out that we are the ones who release the CSP so store it
|
|
// locally into the class object.
|
|
//
|
|
|
|
Assert(m_hProv == NULL);
|
|
m_hProv = psi->hProv;
|
|
psi->hProv = NULL;
|
|
|
|
//
|
|
// Extract out the bulk encryption algorithm we are going to apply to
|
|
// the body of the message. This algorithm is the same across all
|
|
// the different key transfer algorthms.
|
|
//
|
|
|
|
//
|
|
// Setup the structure containing all of the encryption parameters for the
|
|
// Message Encode function. This structure gets setup differently
|
|
// depending on the version of Crypt32 which we are running on.
|
|
//
|
|
|
|
memset(&ceei, 0, sizeof(ceei));
|
|
ceei.cbSize = sizeof(CMSG_ENVELOPED_ENCODE_INFO);
|
|
ceei.hCryptProv = m_hProv;
|
|
ceei.ContentEncryptionAlgorithm = m_psldData->m_ContentEncryptAlgorithm;
|
|
ceei.pvEncryptionAuxInfo = m_psldData->m_pvEncryptAuxInfo;
|
|
|
|
if (g_FSupportV3) {
|
|
ceei.cRecipients = m_psldData->m_cEncryptItems;
|
|
ceei.rgCmsRecipients = m_psldData->m_rgRecipientInfo;
|
|
|
|
if (m_psldData->m_blobUnprotectAttrs.cbData > 0) {
|
|
CHECKHR(hr = HrDecodeObject(m_psldData->m_blobUnprotectAttrs.pbData,
|
|
m_psldData->m_blobUnprotectAttrs.cbData,
|
|
szOID_Microsoft_Attribute_Sequence, 0,
|
|
&cbData, (LPVOID *) &pattrsUnprot));
|
|
ceei.cUnprotectedAttr = pattrsUnprot->cAttr;
|
|
ceei.rgUnprotectedAttr = pattrsUnprot->rgAttr;
|
|
}
|
|
|
|
//
|
|
// Allow for certificates to be carried on the encryption package now
|
|
// need this for Fortezza static-static implementation.
|
|
//
|
|
|
|
if (m_psldData->m_hstoreEncrypt != NULL) {
|
|
hr = _InitEncodedCert(m_psldData->m_hstoreEncrypt, &rgCertBlob, &cCerts,
|
|
&rgCrlBlob, &cCrls);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
ceei.cCertEncoded = cCerts;
|
|
ceei.rgCertEncoded = rgCertBlob;
|
|
ceei.cCrlEncoded = cCrls;
|
|
ceei.rgCrlEncoded = rgCrlBlob;
|
|
}
|
|
}
|
|
else {
|
|
PCERT_INFO pinfo;
|
|
|
|
if (!MemAlloc((LPVOID *) &ceei.rgpRecipients,
|
|
(sizeof(CERT_INFO) + sizeof(PCERT_INFO)) * m_psldData->m_cEncryptItems)) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
memset(ceei.rgpRecipients, 0,
|
|
(sizeof(CERT_INFO) + sizeof(PCERT_INFO)) * m_psldData->m_cEncryptItems);
|
|
ceei.cRecipients = m_psldData->m_cEncryptItems;
|
|
pinfo = (PCERT_INFO) ((ceei.cRecipients * sizeof(PCERT_INFO)) +
|
|
(LPBYTE) ceei.rgpRecipients);
|
|
|
|
for (i=0; i<ceei.cRecipients; i++, pinfo++) {
|
|
ceei.rgpRecipients[i] = pinfo;
|
|
|
|
Assert(m_psldData->m_rgRecipientInfo[i].dwRecipientChoice == CMSG_KEY_TRANS_RECIPIENT);
|
|
|
|
pinfo->SubjectPublicKeyInfo.Algorithm = m_psldData->m_rgRecipientInfo[i].pKeyTrans->KeyEncryptionAlgorithm;
|
|
pinfo->SubjectPublicKeyInfo.PublicKey = m_psldData->m_rgRecipientInfo[i].pKeyTrans->RecipientPublicKey;
|
|
|
|
|
|
Assert(m_psldData->m_rgRecipientInfo[i].pKeyTrans->RecipientId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER);
|
|
pinfo->Issuer = m_psldData->m_rgRecipientInfo[i].pKeyTrans->RecipientId.IssuerSerialNumber.Issuer;
|
|
pinfo->SerialNumber = m_psldData->m_rgRecipientInfo[i].pKeyTrans->RecipientId.IssuerSerialNumber.SerialNumber;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do we need to recurse and wrap ourselves in an Outer Layer?
|
|
if (m_psldData->m_psldOuter) {
|
|
CSSDOUT("Streaming wrapped message (type: %x)", m_psldData->m_psldOuter->m_dwMsgEnhancement);
|
|
|
|
hr = InitInner(psi, NULL, m_psldData->m_psldOuter);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
// This will write the header to the new inner CAPI stream
|
|
if (m_pstmOut) {
|
|
CONVINITINFO ci = {0};
|
|
|
|
// Create a conversion stream
|
|
ci.ietEncoding = IET_BASE64;
|
|
ci.fEncoder = TRUE;
|
|
TrapError(HrCreateInternetConverter(&ci, &m_pConverter));
|
|
|
|
m_pstmOut->Write(s_cszMimeHeader, sizeof(s_cszMimeHeader)-1, NULL);
|
|
if (m_psldData->m_dwMsgEnhancement & MST_THIS_ENCRYPT) {
|
|
m_pstmOut->Write(STR_SMT_ENVELOPEDDATA,
|
|
lstrlen(STR_SMT_ENVELOPEDDATA), NULL);
|
|
}
|
|
else {
|
|
if ((psi->pszInnerContent != NULL) &&
|
|
(strcmp(psi->pszInnerContent,
|
|
szOID_SMIME_ContentType_Receipt) == 0)) {
|
|
m_pstmOut->Write(STR_SMT_SIGNEDRECEIPT,
|
|
lstrlen(STR_SMT_SIGNEDRECEIPT), NULL);
|
|
}
|
|
else {
|
|
m_pstmOut->Write(STR_SMT_SIGNEDDATA,
|
|
lstrlen(STR_SMT_SIGNEDDATA), NULL);
|
|
}
|
|
}
|
|
|
|
m_pstmOut->Write(c_szCRLF, lstrlen(c_szCRLF), NULL);
|
|
m_pstmOut->Write(STR_HDR_CNTXFER, lstrlen(STR_HDR_CNTXFER), NULL);
|
|
m_pstmOut->Write(c_szColonSpace, lstrlen(c_szColonSpace), NULL);
|
|
if (m_pConverter) {
|
|
m_pstmOut->Write(STR_ENC_BASE64, lstrlen(STR_ENC_BASE64), NULL);
|
|
} else {
|
|
// Failed to create the conversion stream. Try sending binary anyway.
|
|
// (Netscape can't read it, but most others can.)
|
|
m_pstmOut->Write(STR_ENC_BINARY, lstrlen(STR_ENC_BINARY), NULL);
|
|
}
|
|
m_pstmOut->Write(c_szCRLF, lstrlen(c_szCRLF), NULL);
|
|
m_pstmOut->Write(s_cszMimeHeader2, sizeof(s_cszMimeHeader2)-1, NULL);
|
|
m_pstmOut->Write(c_szCRLFCRLF, lstrlen(c_szCRLFCRLF), NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Since the write code is so bad for buffering, lets do the buffering here.
|
|
// Ignore all errors on return, if the buffer is not allocated then we just
|
|
// get the same poor performance as before.
|
|
//
|
|
|
|
MemAlloc((LPVOID *) &m_pbBuffer, CbCacheBufferSize);
|
|
|
|
//
|
|
//
|
|
|
|
if (psi->pszInnerContent != NULL) {
|
|
cmsi.cbContent = psi->cbInnerContent;
|
|
}
|
|
else {
|
|
cmsi.cbContent = (DWORD) -1; // indefinite-lenght BER encoding
|
|
}
|
|
cmsi.pfnStreamOutput = CBStreamOutput;
|
|
cmsi.pvArg = (void *)this;
|
|
|
|
m_hMsg = CryptMsgOpenToEncode(
|
|
CRYPT_ASN_ENCODING|PKCS_7_ASN_ENCODING,
|
|
CMSG_CMS_ENCAPSULATED_CONTENT_FLAG | dwFlags,
|
|
dwMsgType,
|
|
// (dwMsgType == CMSG_SIGNED) ? ((void *) &csei) : ((void *) &ceei), // really depends on the union
|
|
&ceei,
|
|
psi->pszInnerContent,
|
|
&cmsi);
|
|
if (! m_hMsg) {
|
|
goto gle;
|
|
}
|
|
|
|
//
|
|
// Put the top level into either DETACHED or STREAM based on if we are
|
|
// doing detached signing or blob signing/encryption.
|
|
//
|
|
// Put the low level stream into the write through state so it moves all
|
|
// data out to the output stream. (If no output stream then mark as no
|
|
// output streaming.)
|
|
//
|
|
|
|
m_csStatus = (m_dwFlagsStm & CSTM_DETACHED) ? STREAM_DETACHED_OCCURING : STREAM_OCCURING;
|
|
m_csStream = m_pstmOut ? CSTM_STREAMING : CSTM_GOTTYPE;
|
|
hr = S_OK;
|
|
|
|
exit:
|
|
if (!g_FSupportV3 && (dwPsiType & MST_THIS_ENCRYPT)) {
|
|
MemFree(ceei.rgpRecipients);
|
|
}
|
|
|
|
ReleaseMem(pKPI);
|
|
if (rgCertBlob) {
|
|
g_pMoleAlloc->Free(rgCertBlob); //also rgpCertInfo
|
|
}
|
|
if (rgCrlBlob) {
|
|
g_pMoleAlloc->Free(rgCrlBlob);
|
|
}
|
|
if (rgpcaps != NULL) {
|
|
for (iSigner=0; iSigner<cSigners; iSigner++) {
|
|
MemFree(rgpcaps[iSigner]);
|
|
}
|
|
MemFree(rgpcaps);
|
|
}
|
|
SafeMemFree(rgSigner);
|
|
SafeMemFree(pattrsUnprot);
|
|
for (iSigner=0; iSigner<cSigners; iSigner++) {
|
|
SafeMemFree(rgpattrAuth[iSigner]);
|
|
SafeMemFree(rgpattrUnauth[iSigner]);
|
|
}
|
|
SafeMemFree(rgpattrAuth);
|
|
SafeMemFree(rgpattrUnauth);
|
|
|
|
Assert(m_hMsg || S_OK != hr);
|
|
return TrapError(hr);
|
|
gle:
|
|
hr = HrGetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
HRESULT CCAPIStm::BeginDecodeStreaming(
|
|
SMIMEINFO *const psi)
|
|
{
|
|
CMSG_STREAM_INFO cmsi;
|
|
|
|
if (CSTM_TYPE_ONLY & m_dwFlagsStm) {
|
|
if (CSTM_DETACHED & m_dwFlagsStm) {
|
|
return E_INVALIDARG;
|
|
}
|
|
}
|
|
|
|
m_dwFlagsStm |= CSTM_DECODE;
|
|
|
|
if (psi) {
|
|
// SMIME3: if there is a cert in the associated layer data, duplicate it up here.
|
|
// BUGBUG: NYI
|
|
|
|
m_hProv = psi->hProv;
|
|
psi->hProv = NULL;
|
|
|
|
// Copy the array of cert stores up here
|
|
if (psi->cStores) {
|
|
m_rgStores = (HCERTSTORE*)g_pMalloc->Alloc(psi->cStores * sizeof(HCERTSTORE));
|
|
if (! m_rgStores) {
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
|
|
for (DWORD i = 0; i < psi->cStores; i++) {
|
|
m_rgStores[i] = CertDuplicateStore(psi->rgStores[i]);
|
|
}
|
|
m_cStores = psi->cStores;
|
|
}
|
|
// HACK!!! HACK!!!! for WIN64
|
|
#ifndef _WIN64
|
|
#ifdef SMIME_V3
|
|
UNALIGNED WCHAR *wsz = psi->pwszKeyPrompt;
|
|
if (wsz != NULL) {
|
|
m_pwszKeyPrompt = PszDupW((LPCWSTR) wsz);
|
|
if (m_pwszKeyPrompt == NULL) {
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
#endif // SMIME_V3
|
|
#endif //_WIN64
|
|
}
|
|
|
|
|
|
cmsi.cbContent = (DWORD)-1; // indefinite-length BER encoding
|
|
cmsi.pfnStreamOutput = CBStreamOutput;
|
|
cmsi.pvArg = (void *)this;
|
|
|
|
m_hMsg = CryptMsgOpenToDecode(
|
|
CRYPT_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
(m_dwFlagsStm & CSTM_DETACHED) ? CMSG_DETACHED_FLAG : 0,
|
|
0, // don't know the type
|
|
m_hProv, // needed for verify, but not decrypt
|
|
NULL, // pRecipientInfo
|
|
&cmsi);
|
|
|
|
if (m_hMsg) {
|
|
m_csStatus = (m_dwFlagsStm & CSTM_DETACHED) ? STREAM_DETACHED_OCCURING : STREAM_QUESTION_TIME;
|
|
} else {
|
|
m_csStatus = STREAM_ERROR;
|
|
}
|
|
|
|
Assert(m_hMsg || S_OK != HrGetLastError());
|
|
return m_hMsg ? S_OK : HrGetLastError();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Callback and helpers/crackers
|
|
//
|
|
|
|
|
|
BOOL WINAPI CCAPIStm::CBStreamOutput(
|
|
const void *pvArg,
|
|
BYTE *pbData,
|
|
DWORD cbData,
|
|
BOOL fFinal)
|
|
{
|
|
Assert(pvArg);
|
|
return((CCAPIStm*)pvArg)->StreamOutput(pbData, cbData, fFinal);
|
|
}
|
|
|
|
|
|
BOOL CCAPIStm::StreamOutput(
|
|
BYTE * pbData,
|
|
DWORD cbData,
|
|
BOOL fFinal)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iEOH;
|
|
#ifdef SMIME_V3
|
|
LPSTR szContentType = NULL;
|
|
#endif // SMIME_V3
|
|
|
|
// m_csStream should be one of the CSTM states at this point, if not then
|
|
// we are in error.
|
|
|
|
Assert((m_csStream == CSTM_GOTTYPE) || (m_csStream == CSTM_FIRST_WRITE) ||
|
|
(m_csStream == CSTM_TEST_NESTING) || (m_csStream == CSTM_STREAMING));
|
|
|
|
// If all we are doing is looking for the type, then we know that
|
|
// we already have one at this point. There is no need to put the
|
|
// output of the Crypto code anyplace as it is not part of what we
|
|
// are looking for.
|
|
|
|
if (CSTM_GOTTYPE == m_csStream) {
|
|
return TRUE;
|
|
}
|
|
|
|
// If we have no output stream, then all we need to do is the state
|
|
// transistion on fFinal being true.
|
|
if (m_pstmOut == NULL) {
|
|
if (fFinal) {
|
|
m_csStream = CSTM_STREAMING_DONE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Test for an enclosed opaque S/MIME message
|
|
// the client doesn't care about this level of goo, so hide it and
|
|
// stream this data into a new CAPIStm, letting it stream out the
|
|
// real stuff.
|
|
//
|
|
|
|
if (CSTM_FIRST_WRITE == m_csStream) {
|
|
// this is the position of the beginning of any
|
|
// possible MIME header
|
|
if (FAILED(HrGetStreamPos(m_pstmOut, &m_cbBeginWrite)) ||
|
|
FAILED(HrGetStreamSize(m_pstmOut, &m_cbBeginSize))) {
|
|
m_cbBeginWrite = 0;
|
|
m_cbBeginSize = 0;
|
|
} else {
|
|
// reset position
|
|
HrStreamSeekSet(m_pstmOut, m_cbBeginWrite);
|
|
}
|
|
|
|
m_csStream = CSTM_TEST_NESTING;
|
|
#ifdef SMIME_V3
|
|
if (szContentType = (LPSTR)PVGetMsgParam(m_hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, NULL, NULL)) {
|
|
if (lstrcmp(szOID_PKCS_7_DATA, szContentType)) {
|
|
hr = m_pstmOut->Write(s_cszOIDMimeHeader1, strlen(s_cszOIDMimeHeader1), NULL);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = m_pstmOut->Write(szContentType, strlen(szContentType), NULL);
|
|
}
|
|
if (SUCCEEDED(hr)) {
|
|
hr = m_pstmOut->Write(s_cszOIDMimeHeader2, strlen(s_cszOIDMimeHeader2), NULL);
|
|
}
|
|
if (FAILED(hr)) {
|
|
return FALSE;
|
|
}
|
|
m_csStream = CSTM_STREAMING;
|
|
}
|
|
}
|
|
#endif // SMIME_V3
|
|
|
|
}
|
|
|
|
if (CSTM_TEST_NESTING == m_csStream &&
|
|
(-1 != (iEOH = SniffForEndOfHeader(pbData, cbData)))) {
|
|
CMimePropertyContainer *pContHeader;
|
|
|
|
// get the position of the first char of the body
|
|
iEOH = cbData - iEOH + 1;
|
|
|
|
pContHeader = new CMimePropertyContainer;
|
|
if (pContHeader) {
|
|
hr = pContHeader->InitNew();
|
|
if (SUCCEEDED(hr)) {
|
|
ULONG posCurrent;
|
|
|
|
// write out the last bit of the header data
|
|
// then move back to the header's start after
|
|
// saving our current position
|
|
hr = m_pstmOut->Write(pbData, iEOH, NULL);
|
|
if (SUCCEEDED(hr)) {
|
|
// fixup the amount of data in pbData so
|
|
// only body stuff gets written to the stream
|
|
// . . . we've already written the header
|
|
pbData += iEOH;
|
|
cbData -= iEOH;
|
|
|
|
HrGetStreamPos(m_pstmOut, &posCurrent);
|
|
HrStreamSeekSet(m_pstmOut, m_cbBeginWrite);
|
|
hr = pContHeader->Load(m_pstmOut);
|
|
#ifdef DEBUG
|
|
BYTE *pbHeader;
|
|
DWORD cbHeader;
|
|
HrStreamToByte(m_pstmOut, &pbHeader, &cbHeader);
|
|
SafeMemFree(pbHeader);
|
|
#endif
|
|
// if we don't have an inner message, need to reset
|
|
// the stream back to where we were
|
|
HrStreamSeekSet(m_pstmOut, posCurrent);
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr)) {
|
|
CSSDOUT("Loaded an inner header.");
|
|
if (IsOpaqueSecureContentType(pContHeader)) {
|
|
CSSDOUT("Sniffed an inner PKCS#7.");
|
|
|
|
// the HandleNesting call will reset m_pstmOut
|
|
TrapError(HandleNesting(pContHeader));
|
|
}
|
|
|
|
m_csStream = CSTM_STREAMING;
|
|
}
|
|
#ifdef DEBUG
|
|
else {
|
|
CSSDOUT("Load of inner header failed.");
|
|
}
|
|
#endif
|
|
pContHeader->Release();
|
|
}
|
|
}
|
|
|
|
if (fFinal) {
|
|
m_csStream = CSTM_STREAMING_DONE;
|
|
}
|
|
|
|
if (m_pConverter) {
|
|
BLOB blob;
|
|
|
|
blob.pBlobData = pbData;
|
|
blob.cbSize = cbData;
|
|
|
|
hr = m_pConverter->HrFillAppend(&blob);
|
|
if (SUCCEEDED(hr)) {
|
|
if (m_dwFlagsStm & CSTM_DECODE) {
|
|
hr = m_pConverter->HrInternetDecode(fFinal);
|
|
}
|
|
else {
|
|
hr = m_pConverter->HrInternetEncode(fFinal);
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr)) {
|
|
hr = m_pConverter->HrWriteConverted(m_pstmOut);
|
|
}
|
|
else {
|
|
hr = m_pstmOut->Write(pbData, cbData, NULL);
|
|
}
|
|
}
|
|
else {
|
|
hr = m_pstmOut->Write(pbData, cbData, NULL);
|
|
}
|
|
|
|
#ifdef SMIME_V3
|
|
MemFree(szContentType);
|
|
#endif // SMIME_V3
|
|
return SUCCEEDED(hr) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
/* SniffForEndOfHeader:
|
|
**
|
|
** Purpose:
|
|
** see if we have accumulated two blank lines in a row
|
|
** Takes:
|
|
** a buffer to scan and size of the buffer
|
|
** Returns:
|
|
** number of characters from the end of the second \n
|
|
*/
|
|
int CCAPIStm::SniffForEndOfHeader(
|
|
BYTE * pbData,
|
|
DWORD cbData)
|
|
{
|
|
BOOL fCR, fEOL;
|
|
|
|
// state is saved b/c the double blank could cross
|
|
// a buffer chunk's boundary
|
|
|
|
// restore old state and also reset
|
|
fCR = m_dwFlagsStm & CSTM_HAVECR;
|
|
fEOL = m_dwFlagsStm & CSTM_HAVEEOL;
|
|
if (fCR || fEOL) {
|
|
m_dwFlagsStm &= ~(CSTM_HAVECR | CSTM_HAVEEOL);
|
|
}
|
|
|
|
while (cbData) {
|
|
if (chCR == *pbData) {
|
|
fCR = TRUE;
|
|
}
|
|
else if (fCR && (chLF == *pbData)) {
|
|
if (fEOL) {
|
|
// double blank line
|
|
return cbData;
|
|
}
|
|
fCR = FALSE;
|
|
fEOL = TRUE;
|
|
}
|
|
else {
|
|
fCR = FALSE;
|
|
fEOL = FALSE;
|
|
}
|
|
pbData++;
|
|
cbData--;
|
|
}
|
|
|
|
// state was reset above. persist if we need to.
|
|
if (fCR || fEOL) {
|
|
m_dwFlagsStm |= (fCR ? CSTM_HAVECR : 0) | (fEOL ? CSTM_HAVEEOL : 0);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
HRESULT CCAPIStm::HandleNesting(CMimePropertyContainer *pContHeader)
|
|
{
|
|
ENCODINGTYPE iet;
|
|
|
|
iet = pContHeader->GetEncodingType();
|
|
if (!(IET_BINARY == iet || IET_7BIT == iet || IET_8BIT == iet)) {
|
|
CONVINITINFO ciiDecode;
|
|
|
|
// we actually need to decode
|
|
|
|
ciiDecode.dwFlags = 0;
|
|
ciiDecode.ietEncoding = iet;
|
|
ciiDecode.fEncoder = FALSE;
|
|
|
|
TrapError(HrCreateInternetConverter(&ciiDecode, &m_pConverter));
|
|
}
|
|
return InitInner();
|
|
}
|
|
|
|
HRESULT CCAPIStm::InitInner()
|
|
{
|
|
SMIMEINFO siBuilt;
|
|
ULARGE_INTEGER liSize;
|
|
|
|
// Init siBuilt
|
|
memset(&siBuilt, 0, sizeof(siBuilt));
|
|
|
|
// now also fixup the stream back to a near original
|
|
// state. if for some reason the data written after
|
|
// now is smaller than the header is, this
|
|
// work will make sure we don't keep bits of the header
|
|
HrStreamSeekSet(m_pstmOut, m_cbBeginWrite);
|
|
liSize.LowPart = m_cbBeginSize;
|
|
liSize.HighPart = m_cbBeginWrite;
|
|
m_pstmOut->SetSize(liSize);
|
|
|
|
siBuilt.hProv = m_hProv;
|
|
|
|
#ifdef OLD_STUFF
|
|
// BUGBUG: Is something like this needed?
|
|
siBuilt.ssEncrypt.pcDecryptionCert = m_pUserCertDecrypt;
|
|
#endif // OLD_STUFF
|
|
|
|
siBuilt.cStores = m_cStores;
|
|
siBuilt.rgStores = m_rgStores;
|
|
return InitInner(&siBuilt);
|
|
}
|
|
|
|
|
|
HRESULT CCAPIStm::InitInner(
|
|
SMIMEINFO *const psi,
|
|
CCAPIStm * pOuter,
|
|
PSECURITY_LAYER_DATA psldOuter)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (! pOuter) {
|
|
m_pCapiInner = new CCAPIStm(m_pstmOut);
|
|
|
|
CHECKHR(hr = m_pCapiInner-> HrInnerInitialize(m_dwFlagsSEF, m_hwnd, m_dwFlagsStm, m_pSmimeCallback, psldOuter));
|
|
|
|
if (!psldOuter) {
|
|
// Hook up the chain of Security Layer Data objects.
|
|
Assert(! m_psldData->m_psldInner);
|
|
m_pCapiInner->m_psldData->AddRef();
|
|
m_psldData->m_psldInner = m_pCapiInner->m_psldData;
|
|
if (m_pCapiInner->m_psldData) {
|
|
// Init the Up pointer of the new layer data
|
|
m_pCapiInner->m_psldData->m_psldOuter = m_psldData;
|
|
}
|
|
}
|
|
|
|
// recurse
|
|
return m_pCapiInner->InitInner(psi, this, psldOuter);
|
|
}
|
|
|
|
Assert(!m_pCapiInner);
|
|
Assert(pOuter);
|
|
Assert(psi);
|
|
|
|
m_dwFlagsStm = pOuter->m_dwFlagsStm & CSTM_ALLFLAGS;
|
|
|
|
|
|
// This will get me involved
|
|
ReleaseObj(pOuter->m_pstmOut);
|
|
pOuter->m_pstmOut = (IStream*)this;
|
|
AddRef(); // outer is holding 1
|
|
|
|
m_dwFlagsStm |= CSTM_RECURSED;
|
|
if (pOuter->m_dwFlagsStm & CSTM_DECODE) {
|
|
hr = BeginDecodeStreaming(psi);
|
|
}
|
|
else {
|
|
// don't support detached inner CRYPTMSGs
|
|
m_dwFlagsStm &= ~CSTM_DETACHED;
|
|
|
|
hr = BeginEncodeStreaming(psi);
|
|
}
|
|
m_dwFlagsStm &= ~CSTM_RECURSED;
|
|
|
|
exit:
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Gets the immediate outermost decryption cert (if any).
|
|
//
|
|
PCCERT_CONTEXT CCAPIStm::GetOuterDecryptCert()
|
|
{
|
|
PCCERT_CONTEXT pccertDecrypt = NULL;
|
|
PSECURITY_LAYER_DATA psldOuter = NULL;
|
|
|
|
Assert(NULL != m_psldData);
|
|
if (NULL != m_psldData) {
|
|
psldOuter = m_psldData->m_psldOuter;
|
|
}
|
|
|
|
while (NULL != psldOuter) {
|
|
if (NULL != psldOuter->m_pccertDecrypt) {
|
|
Assert( MST_ENCRYPT_MASK & (psldOuter->m_dwMsgEnhancement) );
|
|
pccertDecrypt = psldOuter->m_pccertDecrypt;
|
|
break;
|
|
}
|
|
psldOuter = psldOuter->m_psldOuter;
|
|
}
|
|
|
|
return pccertDecrypt;
|
|
}
|
|
|
|
HCERTSTORE
|
|
OpenAllStore(
|
|
IN DWORD cStores,
|
|
IN HCERTSTORE rgStores[],
|
|
IN OUT HCERTSTORE *phCertStoreAddr,
|
|
IN OUT HCERTSTORE *phCertStoreCA,
|
|
IN OUT HCERTSTORE *phCertStoreMy,
|
|
IN OUT HCERTSTORE *phCertStoreRoot
|
|
)
|
|
{
|
|
HCERTSTORE hstoreAll;
|
|
DWORD i;
|
|
|
|
hstoreAll = CertOpenStore(CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING,
|
|
NULL, 0, NULL);
|
|
if (hstoreAll == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
for (i=0; i<cStores; i++) {
|
|
CertAddStoreToCollection(hstoreAll, rgStores[i], 0, 0);
|
|
}
|
|
|
|
// Open the standard system stores
|
|
|
|
*phCertStoreAddr = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING,
|
|
NULL, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
s_cszWABCertStore);
|
|
if (*phCertStoreAddr != NULL) {
|
|
CertAddStoreToCollection(hstoreAll, *phCertStoreAddr, 0, 0);
|
|
}
|
|
|
|
*phCertStoreMy = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER, s_cszMy);
|
|
if (*phCertStoreMy != NULL) {
|
|
CertAddStoreToCollection(hstoreAll, *phCertStoreMy, 0, 0);
|
|
}
|
|
|
|
*phCertStoreCA = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER, s_cszCA);
|
|
if (*phCertStoreCA != NULL) {
|
|
CertAddStoreToCollection(hstoreAll, *phCertStoreCA, 0, 0);
|
|
}
|
|
|
|
*phCertStoreRoot = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER, "Root");
|
|
if (*phCertStoreRoot != NULL) {
|
|
CertAddStoreToCollection(hstoreAll, *phCertStoreRoot, 0, 0);
|
|
}
|
|
|
|
return hstoreAll;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// The enveloped and signed message parsers
|
|
//
|
|
|
|
|
|
/* VerifySignedMessage:
|
|
**
|
|
** Purpose:
|
|
** Using CAPI, this loads the certs from the message and tests to find
|
|
** the one hopefully used to sign the message. CAPI builds a new hash
|
|
** using this cert and compares it with the hash from the SignerInfo.
|
|
** Takes:
|
|
** IN hMsg - built CAPI message containing cyphertext
|
|
** OUT psi - certificate used for signing, and if it was part of hMsg
|
|
** OUT OPTIONAL pPlain - blob containing cleartext
|
|
** Returns:
|
|
** MIME_E_SECURITY_MULTSIGNERS if cSigners > 1. We can't deal.
|
|
** MIME_E_SECURITY_BADCONTENT if I don't understand the message type of the
|
|
** inner data
|
|
** else S_OK or E_FAIL
|
|
*/
|
|
HRESULT CCAPIStm::VerifySignedMessage()
|
|
{
|
|
CRYPT_SMIME_CAPABILITIES cap;
|
|
DWORD cbData = 0;
|
|
DWORD cCerts;
|
|
DWORD cSigners = 0;
|
|
DWORD dexStore;
|
|
BOOL f;
|
|
#ifdef SMIME_V3
|
|
BOOL fLookForReceiptRequest = TRUE;
|
|
#endif // SMIME_V3
|
|
HCERTSTORE hCertStoreAddr = NULL;
|
|
HCERTSTORE hCertStoreCA = NULL;
|
|
HCERTSTORE hCertStoreMy = NULL;
|
|
HCERTSTORE hCertStoreRoot = NULL;
|
|
HCERTSTORE hMsgCertStore = NULL;
|
|
HRESULT hr = S_OK;
|
|
DWORD i;
|
|
DWORD iSigner;
|
|
#ifdef SMIME_V3
|
|
DWORD iSignData;
|
|
CRYPT_ATTR_BLOB attrReceiptReq = {0};
|
|
CRYPT_ATTR_BLOB attrSecLabel = {0};
|
|
DWORD cblabel;
|
|
CMSG_CMS_SIGNER_INFO cmsSignerInfo;
|
|
DWORD dwCtrl;
|
|
HCERTSTORE hstoreAll = NULL;
|
|
PSMIME_RECEIPT_REQUEST preq = NULL;
|
|
PSMIME_SECURITY_LABEL plabel = NULL;
|
|
CMSG_CMS_SIGNER_INFO * pCmsSignerInfo = NULL;
|
|
LPVOID pv;
|
|
CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA verifySignature;
|
|
#endif // SMIME_V3
|
|
PCCERT_CONTEXT pccertSigner = NULL;
|
|
SignerData * pSignerData = NULL;
|
|
PCMSG_SIGNER_INFO pSignerInfo = NULL;
|
|
CERT_INFO SignerId;
|
|
LPSTR szContentType = NULL;
|
|
|
|
Assert(m_hMsg);
|
|
Assert(m_psldData->m_fCertInLayer == FALSE);
|
|
|
|
// Get the number of signers
|
|
cbData = sizeof(cSigners);
|
|
f = CryptMsgGetParam(m_hMsg, CMSG_SIGNER_COUNT_PARAM, 0, &cSigners, &cbData);
|
|
if (!f) {
|
|
goto CryptoError;
|
|
}
|
|
if (cSigners == 0) {
|
|
hr = MIME_E_NO_SIGNER;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// Allocate space to hold the signer information
|
|
|
|
if (!MemAlloc((LPVOID *) &pSignerData, cSigners * sizeof(SignerData))) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto ErrorReturn;
|
|
}
|
|
m_psldData->m_rgSigners = pSignerData;
|
|
m_psldData->m_cSigners = cSigners;
|
|
|
|
// Initialized to a known state
|
|
memset(pSignerData, 0, cSigners * sizeof(SignerData));
|
|
for (i=0; i<cSigners; i++) {
|
|
pSignerData[i].ulValidity = MSV_UNVERIFIABLE;
|
|
}
|
|
|
|
// If there are certificates in the message, get a store provider which
|
|
// maps to the certificates in the message for later lookup.
|
|
|
|
Assert(sizeof(cCerts) == cbData);
|
|
f = CryptMsgGetParam(m_hMsg, CMSG_CERT_COUNT_PARAM, 0, &cCerts, &cbData);
|
|
Assert(f);
|
|
|
|
if (f && cCerts) {
|
|
// since there are certs included, let's try them first when matching
|
|
// certs with signers.
|
|
|
|
// get the store set
|
|
// make sure we keep hold of our provider
|
|
hMsgCertStore = CertOpenStore(CERT_STORE_PROV_MSG, X509_ASN_ENCODING,
|
|
m_hProv, 0, m_hMsg);
|
|
if (hMsgCertStore) {
|
|
m_dwFlagsStm |= CSTM_DONTRELEASEPROV; // given unto the store
|
|
}
|
|
|
|
// if it failed, we just don't have a store then
|
|
Assert(hMsgCertStore != NULL);
|
|
m_psldData->m_hcertstor = CertDuplicateStore(hMsgCertStore);
|
|
}
|
|
|
|
//
|
|
// Walk through each and every signature attempting to verify each signature
|
|
//
|
|
|
|
for (iSigner=0; iSigner<cSigners; iSigner++, pSignerData++) {
|
|
// Preconditions
|
|
Assert(pccertSigner == NULL);
|
|
|
|
// release the signer info from the previous iteration.
|
|
SafeMemFree(pSignerInfo);
|
|
if (pCmsSignerInfo != &cmsSignerInfo) {
|
|
SafeMemFree(pCmsSignerInfo);
|
|
}
|
|
|
|
// get the issuer and serial number from the ith SignerInfo
|
|
if (g_FSupportV3) {
|
|
hr = HrGetMsgParam(m_hMsg, CMSG_CMS_SIGNER_INFO_PARAM, iSigner,
|
|
(LPVOID *) &pCmsSignerInfo, NULL);
|
|
if (FAILED(hr)) {
|
|
goto ErrorReturn;
|
|
}
|
|
}
|
|
else {
|
|
hr = HrGetMsgParam(m_hMsg, CMSG_SIGNER_INFO_PARAM, iSigner,
|
|
(LPVOID *) &pSignerInfo, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorReturn;
|
|
}
|
|
cmsSignerInfo.dwVersion = pSignerInfo->dwVersion;
|
|
cmsSignerInfo.SignerId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
|
|
cmsSignerInfo.SignerId.IssuerSerialNumber.Issuer = pSignerInfo->Issuer;
|
|
cmsSignerInfo.SignerId.IssuerSerialNumber.SerialNumber = pSignerInfo->SerialNumber;
|
|
cmsSignerInfo.HashAlgorithm = pSignerInfo->HashAlgorithm;
|
|
cmsSignerInfo.HashEncryptionAlgorithm = pSignerInfo->HashEncryptionAlgorithm;
|
|
cmsSignerInfo.EncryptedHash = pSignerInfo->EncryptedHash;
|
|
cmsSignerInfo.AuthAttrs = pSignerInfo->AuthAttrs;
|
|
cmsSignerInfo.UnauthAttrs = pSignerInfo->UnauthAttrs;
|
|
|
|
pCmsSignerInfo = &cmsSignerInfo;
|
|
|
|
// (post-SDR)
|
|
// Build up IASN
|
|
|
|
SignerId.Issuer = pSignerInfo->Issuer;
|
|
SignerId.SerialNumber = pSignerInfo->SerialNumber;
|
|
}
|
|
|
|
// Our best bet to easily find a certificate is in the message provided
|
|
// list of certificates.
|
|
|
|
if (hMsgCertStore) {
|
|
if (g_FSupportV3) {
|
|
pccertSigner = CertFindCertificateInStore(hMsgCertStore, X509_ASN_ENCODING, 0,
|
|
CERT_FIND_CERT_ID,
|
|
&pCmsSignerInfo->SignerId, NULL);
|
|
}
|
|
else {
|
|
pccertSigner = CertGetSubjectCertificateFromStore(hMsgCertStore,
|
|
X509_ASN_ENCODING, &SignerId);
|
|
}
|
|
if (pccertSigner != NULL) {
|
|
m_psldData->m_fCertInLayer = TRUE;
|
|
}
|
|
}
|
|
|
|
if (pccertSigner == NULL) {
|
|
if (g_FSupportV3) {
|
|
hstoreAll = OpenAllStore(
|
|
m_cStores,
|
|
m_rgStores,
|
|
&hCertStoreAddr,
|
|
&hCertStoreCA,
|
|
&hCertStoreMy,
|
|
&hCertStoreRoot
|
|
);
|
|
if (hstoreAll == NULL)
|
|
goto CryptoError;
|
|
|
|
pccertSigner = CertFindCertificateInStore(hstoreAll, X509_ASN_ENCODING, 0,
|
|
CERT_FIND_CERT_ID,
|
|
&pCmsSignerInfo->SignerId, NULL);
|
|
}
|
|
else {
|
|
Assert(!g_FSupportV3);
|
|
CSSDOUT("Couldn't find cert in message store");
|
|
// Look in the caller specified cert store before the hard coded stores.
|
|
for (dexStore=0; dexStore<m_cStores; dexStore++) {
|
|
if (m_rgStores[dexStore]) {
|
|
if (pccertSigner = CertGetSubjectCertificateFromStore(
|
|
m_rgStores[dexStore], X509_ASN_ENCODING, &SignerId)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pccertSigner) {
|
|
// Look in the "Address Book" store
|
|
if (hCertStoreAddr == NULL) {
|
|
hCertStoreAddr = CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
|
|
X509_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
s_cszWABCertStore);
|
|
}
|
|
if (hCertStoreAddr != NULL) {
|
|
pccertSigner = CertGetSubjectCertificateFromStore(
|
|
hCertStoreAddr, X509_ASN_ENCODING, &SignerId);
|
|
}
|
|
}
|
|
|
|
if (!pccertSigner) {
|
|
// Look in the "My" store
|
|
if (hCertStoreMy == NULL) {
|
|
hCertStoreMy = CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
|
|
X509_ASN_ENCODING, NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER,
|
|
s_cszMy);
|
|
}
|
|
if (hCertStoreMy != NULL) {
|
|
pccertSigner = CertGetSubjectCertificateFromStore(
|
|
hCertStoreMy, X509_ASN_ENCODING, &SignerId);
|
|
}
|
|
}
|
|
|
|
if (!pccertSigner) {
|
|
// Look in the "CA" store
|
|
if (hCertStoreCA == NULL) {
|
|
hCertStoreCA = CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
|
|
X509_ASN_ENCODING, NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER, s_cszCA);
|
|
}
|
|
|
|
if (hCertStoreCA) {
|
|
pccertSigner = CertGetSubjectCertificateFromStore(
|
|
hCertStoreCA, X509_ASN_ENCODING, &SignerId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// By now we should have a certificate to verify with, if we don't then
|
|
// we need to say we can't do anything with it.
|
|
//
|
|
|
|
if (!pccertSigner) {
|
|
// we still can't find the cert. Therefore, cannot verify signer
|
|
CSSDOUT("Cannot verify signer");
|
|
pSignerData->ulValidity = MSV_UNVERIFIABLE;
|
|
} else {
|
|
pSignerData->pccert = CertDuplicateCertificateContext(pccertSigner);
|
|
|
|
if (g_FSupportV3) {
|
|
dwCtrl = CMSG_CTRL_VERIFY_SIGNATURE_EX;
|
|
pv = &verifySignature;
|
|
|
|
verifySignature.cbSize = sizeof(verifySignature);
|
|
verifySignature.hCryptProv = NULL;
|
|
verifySignature.dwSignerIndex = iSigner;
|
|
verifySignature.dwSignerType = CMSG_VERIFY_SIGNER_CERT;
|
|
verifySignature.pvSigner = (LPVOID) pccertSigner;
|
|
}
|
|
else {
|
|
dwCtrl = CMSG_CTRL_VERIFY_SIGNATURE;
|
|
pv = pccertSigner->pCertInfo;
|
|
}
|
|
|
|
retry:
|
|
if (!CryptMsgControl(m_hMsg, 0, dwCtrl, pv)) {
|
|
HRESULT hr2 = HrGetLastError();
|
|
CSSDOUT("Failed signer verify --> %lx", hr2);
|
|
|
|
if (hr2 == CRYPT_E_MISSING_PUBKEY_PARA) {
|
|
if (NULL == hstoreAll && g_FSupportV3) {
|
|
hstoreAll = OpenAllStore(
|
|
m_cStores,
|
|
m_rgStores,
|
|
&hCertStoreAddr,
|
|
&hCertStoreCA,
|
|
&hCertStoreMy,
|
|
&hCertStoreRoot
|
|
);
|
|
if (NULL == hstoreAll)
|
|
goto CryptoError;
|
|
}
|
|
hr2 = GetParameters(pccertSigner, hMsgCertStore, hstoreAll);
|
|
if (hr2 == S_OK) {
|
|
goto retry;
|
|
}
|
|
pSignerData->ulValidity = MSV_UNVERIFIABLE;
|
|
}
|
|
else if (NTE_BAD_SIGNATURE == hr2 || CRYPT_E_HASH_VALUE == hr2) {
|
|
pSignerData->ulValidity = MSV_BADSIGNATURE;
|
|
} else if (NTE_BAD_ALGID == hr2) {
|
|
pSignerData->ulValidity = MSV_UNKHASH;
|
|
} else if (CRYPT_E_SIGNER_NOT_FOUND == hr2) {
|
|
pSignerData->ulValidity = MSV_UNVERIFIABLE;
|
|
} else if (NTE_FAIL == hr2) {
|
|
// RSABASE returns errors. This might
|
|
// be a failure or the hash might be changed.
|
|
// Have to be cautious -> make it bad.
|
|
pSignerData->ulValidity = MSV_BADSIGNATURE;
|
|
} else {
|
|
pSignerData->ulValidity = MSV_MALFORMEDSIG;
|
|
}
|
|
} else {
|
|
CSSDOUT("Verify of signature succeeded.");
|
|
pSignerData->ulValidity &=
|
|
~(MSV_SIGNATURE_MASK|MSV_SIGNING_MASK);
|
|
}
|
|
|
|
// Determine if certificate is expired
|
|
if (0 != CertVerifyTimeValidityWithDelta(NULL, pccertSigner->pCertInfo, TIME_DELTA_SECONDS)) {
|
|
pSignerData->ulValidity |= MSV_EXPIRED_SIGNINGCERT;
|
|
}
|
|
}
|
|
|
|
|
|
if (szContentType = (LPSTR)PVGetMsgParam(m_hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, NULL, NULL)) {
|
|
if (lstrcmp(szOID_PKCS_7_DATA, szContentType)) {
|
|
CSSDOUT("Guess what, we have nested PKCS7 data types (maybe).");
|
|
}
|
|
} else {
|
|
// CAPI failed... we are in trouble...
|
|
pSignerData->ulValidity |= MSV_INVALID;
|
|
|
|
hr = MIME_E_SECURITY_BADCONTENT;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// Grab the hashing alg
|
|
cap.cCapability = 1;
|
|
cap.rgCapability = (CRYPT_SMIME_CAPABILITY *) &pCmsSignerInfo->HashAlgorithm;
|
|
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, PKCS_SMIME_CAPABILITIES,
|
|
&cap, CRYPT_ENCODE_ALLOC_FLAG, &CryptEncodeAlloc,
|
|
&pSignerData->blobHashAlg.pBlobData,
|
|
&pSignerData->blobHashAlg.cbSize)) {
|
|
Assert(FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the attributes, authenicated and unauthenicated, and put into the
|
|
// structure so we can push them back to the user later
|
|
if (pCmsSignerInfo->AuthAttrs.cAttr != 0) {
|
|
#ifdef SMIME_V3
|
|
for (i=0; i<pCmsSignerInfo->AuthAttrs.cAttr; i++) {
|
|
// If we have a security label in this message, then we need to
|
|
// perform access validation.
|
|
if (g_FSupportV3 && FIsMsasn1Loaded()) {
|
|
if (strcmp(pCmsSignerInfo->AuthAttrs.rgAttr[i].pszObjId,
|
|
szOID_SMIME_Security_Label) == 0) {
|
|
|
|
if ((pSignerData->ulValidity & MSV_SIGNATURE_MASK) != MSV_OK) {
|
|
DWORD dw = DwProcessLabelWithCertError();
|
|
if (CertErrorProcessLabelGrant == dw) {
|
|
hr = S_OK;
|
|
continue;
|
|
}
|
|
else if (CertErrorProcessLabelDeny == dw) {
|
|
hr = MIME_E_SECURITY_LABELACCESSDENIED;
|
|
goto ErrorReturn;
|
|
}
|
|
// else continue processing the label.
|
|
}
|
|
|
|
if (pCmsSignerInfo->AuthAttrs.rgAttr[i].cValue != 1) {
|
|
hr = MIME_E_SECURITY_LABELCORRUPT;
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// Have we already seen a label?
|
|
if (attrSecLabel.pbData != NULL) {
|
|
// Check that the one we saw matches this one
|
|
if ((attrSecLabel.cbData !=
|
|
pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].cbData) ||
|
|
memcmp(attrSecLabel.pbData,
|
|
pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].pbData,
|
|
attrSecLabel.cbData)) {
|
|
if (FHideMsgWithDifferentLabels()) {
|
|
hr = MIME_E_SECURITY_LABELCORRUPT;
|
|
goto ErrorReturn;
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
}
|
|
else {
|
|
// Save label.
|
|
attrSecLabel.cbData = pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].cbData;
|
|
if (!MemAlloc((LPVOID*) (& attrSecLabel.pbData), attrSecLabel.cbData)) {
|
|
hr = MIME_E_SECURITY_LABELCORRUPT;
|
|
goto ErrorReturn;
|
|
}
|
|
memcpy(attrSecLabel.pbData, pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].pbData,
|
|
attrSecLabel.cbData);
|
|
}
|
|
|
|
// Clean-up from last loop
|
|
if (plabel != NULL) CryptDecodeAlloc.pfnFree(plabel);
|
|
|
|
// Crack the contents of the label
|
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_SMIME_Security_Label,
|
|
pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].pbData,
|
|
pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].cbData,
|
|
CRYPT_ENCODE_ALLOC_FLAG, &CryptDecodeAlloc,
|
|
&plabel, &cblabel)) {
|
|
goto CryptoError;
|
|
}
|
|
|
|
// Query the policy.
|
|
hr = HrCheckLabelAccess((m_dwFlagsSEF & SEF_NOUI) ?
|
|
SMIME_POLICY_MODULE_NOUI: 0,
|
|
m_hwnd, plabel, GetOuterDecryptCert(),
|
|
pccertSigner, hMsgCertStore);
|
|
|
|
|
|
// If security policy returned an error, then abort.
|
|
if (FAILED(hr)) {
|
|
goto ErrorReturn;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_FSupportV3 && FIsMsasn1Loaded() && (fLookForReceiptRequest) &&
|
|
((pSignerData->ulValidity & (MSV_SIGNATURE_MASK | MSV_SIGNING_MASK)) == MSV_OK)) {
|
|
// If we have a receipt request in this message than we need to build
|
|
// the receipt body now while we have a chance.
|
|
|
|
if (strcmp(pCmsSignerInfo->AuthAttrs.rgAttr[i].pszObjId,
|
|
szOID_SMIME_Receipt_Request) == 0) {
|
|
if (pCmsSignerInfo->AuthAttrs.rgAttr[i].cValue != 1) {
|
|
if (attrReceiptReq.pbData != NULL) {
|
|
StopSendOfReceipt:
|
|
for (iSignData=0; iSignData < iSigner; iSignData++) {
|
|
SafeMemFree(m_psldData->m_rgSigners[iSignData].blobReceipt.pBlobData);
|
|
SafeMemFree(m_psldData->m_rgSigners[iSignData].blobHash.pBlobData);
|
|
m_psldData->m_rgSigners[iSignData].blobReceipt.cbSize = 0;
|
|
m_psldData->m_rgSigners[iSignData].blobHash.cbSize = 0;
|
|
}
|
|
}
|
|
fLookForReceiptRequest = FALSE;
|
|
continue;
|
|
}
|
|
|
|
DWORD cb;
|
|
DWORD cbReceipt;
|
|
DWORD cbHash = 0;
|
|
LPBYTE pbReceipt = NULL;
|
|
LPBYTE pbHash = NULL;
|
|
SMIME_RECEIPT receipt = {0};
|
|
|
|
// Clean-up from last loop
|
|
if (preq != NULL) free(preq);
|
|
|
|
// Have we already seen a receipt?
|
|
if (attrReceiptReq.pbData != NULL) {
|
|
// Check that the one we saw matches this one
|
|
if ((attrReceiptReq.cbData !=
|
|
pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].cbData) ||
|
|
memcmp(attrReceiptReq.pbData,
|
|
pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].pbData,
|
|
attrReceiptReq.cbData)) {
|
|
goto StopSendOfReceipt;
|
|
}
|
|
}
|
|
else {
|
|
// Save receipt request
|
|
attrReceiptReq.cbData = pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].cbData;
|
|
if (!MemAlloc((LPVOID*) (& attrReceiptReq.pbData), attrReceiptReq.cbData)) {
|
|
// abort looking for receipt requests.
|
|
goto StopSendOfReceipt;
|
|
}
|
|
memcpy(attrReceiptReq.pbData, pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].pbData,
|
|
attrReceiptReq.cbData);
|
|
}
|
|
|
|
// Crack the contents of the receipt
|
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_SMIME_Receipt_Request,
|
|
pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].pbData,
|
|
pCmsSignerInfo->AuthAttrs.rgAttr[i].rgValue[0].cbData,
|
|
CRYPT_ENCODE_ALLOC_FLAG, &CryptDecodeAlloc,
|
|
&preq, &cb)) {
|
|
goto StopSendOfReceipt;
|
|
}
|
|
|
|
// Encode the receipt
|
|
|
|
receipt.Version = 1;
|
|
receipt.pszOIDContent = szContentType;
|
|
receipt.ContentIdentifier = preq->ContentIdentifier;
|
|
receipt.OriginatorSignature.cbData = pCmsSignerInfo->EncryptedHash.cbData;
|
|
receipt.OriginatorSignature.pbData = pCmsSignerInfo->EncryptedHash.pbData;
|
|
|
|
if (!CryptEncodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_SMIME_ContentType_Receipt,
|
|
&receipt, CRYPT_ENCODE_ALLOC_FLAG,
|
|
&CryptEncodeAlloc, &pbReceipt,
|
|
&cbReceipt)) {
|
|
goto StopSendOfReceipt;
|
|
}
|
|
|
|
pSignerData->blobReceipt.cbSize = cbReceipt;
|
|
pSignerData->blobReceipt.pBlobData = pbReceipt;
|
|
|
|
pbHash = (LPBYTE)PVGetMsgParam(m_hMsg, CMSG_COMPUTED_HASH_PARAM,
|
|
NULL, &cbHash);
|
|
if (pbHash == NULL) {
|
|
goto CryptoError;
|
|
}
|
|
pSignerData->blobHash.cbSize = cbHash;
|
|
pSignerData->blobHash.pBlobData = pbHash;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // SMIME_V3
|
|
//
|
|
|
|
cbData = 0;
|
|
LPBYTE pb;
|
|
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, szOID_Microsoft_Attribute_Sequence,
|
|
&pCmsSignerInfo->AuthAttrs, CRYPT_ENCODE_ALLOC_FLAG,
|
|
&CryptEncodeAlloc, &pb, &cbData)) {
|
|
goto CryptoError;
|
|
}
|
|
pSignerData->blobAuth.cbSize = cbData;
|
|
pSignerData->blobAuth.pBlobData = pb;
|
|
}
|
|
|
|
if (pCmsSignerInfo->UnauthAttrs.cAttr != 0) {
|
|
LPBYTE pb;
|
|
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, szOID_Microsoft_Attribute_Sequence,
|
|
&pCmsSignerInfo->UnauthAttrs,
|
|
CRYPT_ENCODE_ALLOC_FLAG, &CryptEncodeAlloc,
|
|
&pb, &cbData)) {
|
|
goto CryptoError;
|
|
}
|
|
|
|
pSignerData->blobUnauth.cbSize = cbData;
|
|
pSignerData->blobUnauth.pBlobData = pb;
|
|
}
|
|
|
|
CertFreeCertificateContext(pccertSigner);
|
|
pccertSigner = NULL;
|
|
}
|
|
|
|
exit:
|
|
#ifdef SMIME_V3
|
|
if (preq != NULL) CryptDecodeAlloc.pfnFree(preq);
|
|
if (plabel != NULL) CryptDecodeAlloc.pfnFree(plabel);
|
|
SafeMemFree(attrReceiptReq.pbData);
|
|
SafeMemFree(attrSecLabel.pbData);
|
|
#endif // SMIME_V3
|
|
if (hCertStoreAddr != NULL) CertCloseStore(hCertStoreAddr, 0);
|
|
if (hCertStoreMy != NULL) CertCloseStore(hCertStoreMy, 0);
|
|
if (hCertStoreCA != NULL) CertCloseStore(hCertStoreCA, 0);
|
|
if (hCertStoreRoot != NULL) CertCloseStore(hCertStoreRoot, 0);
|
|
if (hMsgCertStore != NULL) CertCloseStore(hMsgCertStore, 0);
|
|
MemFree(szContentType);
|
|
ReleaseMem(pSignerInfo);
|
|
#ifdef SMIME_V3
|
|
if (pCmsSignerInfo != &cmsSignerInfo) {
|
|
ReleaseMem(pCmsSignerInfo);
|
|
}
|
|
if (hstoreAll != NULL) CertCloseStore(hstoreAll, 0);
|
|
#endif // SMIME_V3
|
|
ReleaseCert(pccertSigner);
|
|
return hr;
|
|
|
|
CryptoError:
|
|
hr = HrGetLastError();
|
|
|
|
ErrorReturn:
|
|
// On error, release the cert store
|
|
if (m_psldData->m_hcertstor != NULL) {
|
|
CertCloseStore(m_psldData->m_hcertstor, 0);
|
|
m_psldData->m_hcertstor = NULL;
|
|
}
|
|
|
|
|
|
if (S_OK == hr)
|
|
// our generic error message
|
|
hr = TrapError(MIME_E_SECURITY_BADMESSAGE);
|
|
goto exit;
|
|
}
|
|
|
|
static HRESULT GetCSP(PCCERT_CONTEXT pccert, HCRYPTPROV * phprov, DWORD * pdwKeyId)
|
|
{
|
|
HRESULT hr;
|
|
PCRYPT_KEY_PROV_INFO pKPI = NULL;
|
|
|
|
Assert(*phprov == NULL);
|
|
Assert(*pdwKeyId == 0);
|
|
|
|
//
|
|
//
|
|
|
|
hr = HrGetCertificateParam(pccert, CERT_KEY_PROV_INFO_PROP_ID,
|
|
(LPVOID *) &pKPI, NULL);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
*pdwKeyId = pKPI->dwKeySpec;
|
|
|
|
// If the cert specifies the base provider OR has no specification,
|
|
// then try to acquire RSAENH, else get RSABASE.
|
|
|
|
if ((PROV_RSA_FULL == pKPI->dwProvType) &&
|
|
(UnlocStrEqNW(pKPI->pwszProvName, MS_DEF_PROV_W,
|
|
sizeof(MS_DEF_PROV_W)/sizeof(WCHAR)-5) ||
|
|
(*pKPI->pwszProvName == 0))) {
|
|
if (!CRYPT_ACQUIRE_CONTEXT(phprov, pKPI->pwszContainerName,
|
|
MS_ENHANCED_PROV_W, PROV_RSA_FULL,
|
|
pKPI->dwFlags)) {
|
|
CSSDOUT("CryptAcquireContext -> %x\n", HrGetLastError());
|
|
}
|
|
}
|
|
|
|
if (*phprov == NULL) {
|
|
if (! CRYPT_ACQUIRE_CONTEXT(phprov, pKPI->pwszContainerName,
|
|
pKPI->pwszProvName, pKPI->dwProvType,
|
|
pKPI->dwFlags)) {
|
|
CSSDOUT("CryptAcquireContext -> %x\n", HrGetLastError());
|
|
hr = HrGetLastError();
|
|
goto exit;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
exit:
|
|
ReleaseMem(pKPI);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCAPIStm::FindKeyFor(HWND hwnd, DWORD dwFlags, DWORD dwRecipientIndex,
|
|
const CMSG_CMS_RECIPIENT_INFO * pRecipInfo,
|
|
HCERTSTORE hcertstore, DWORD * pdwCtrl,
|
|
CMS_CTRL_DECRYPT_INFO * pDecryptInfo,
|
|
PCCERT_CONTEXT * ppCertDecrypt)
|
|
{
|
|
HRESULT hr;
|
|
HCRYPTPROV hProv = NULL;
|
|
DWORD i;
|
|
PCMSG_CTRL_DECRYPT_PARA pccdp;
|
|
PCCERT_CONTEXT pCertDecrypt = NULL;
|
|
PCCERT_CONTEXT pCertOrig = NULL;
|
|
|
|
if (g_FSupportV3) {
|
|
switch (pRecipInfo->dwRecipientChoice) {
|
|
//
|
|
// Given the certificate reference, see if we can find it in
|
|
// the passed in certificate stores, if yes then we will attempt
|
|
// to decrypt using that certificate
|
|
//
|
|
// This is a Key Transport recipient info object. The CAPI 2.0
|
|
// code can deal with both SKI and Issuer/Serial Number references
|
|
//
|
|
|
|
case CMSG_KEY_TRANS_RECIPIENT:
|
|
pCertDecrypt = CertFindCertificateInStore(hcertstore, X509_ASN_ENCODING,
|
|
0, CERT_FIND_CERT_ID,
|
|
&pRecipInfo->pKeyTrans->RecipientId, NULL);
|
|
|
|
if (pCertDecrypt != NULL) {
|
|
hr = GetCSP(pCertDecrypt, &pDecryptInfo->trans.hCryptProv,
|
|
&pDecryptInfo->trans.dwKeySpec);
|
|
if (SUCCEEDED(hr)) {
|
|
//
|
|
// We find a certificate for this lock box. Setup the
|
|
// structure to be used in decrypting the message.
|
|
//
|
|
|
|
*pdwCtrl = CMSG_CTRL_KEY_TRANS_DECRYPT;
|
|
pDecryptInfo->trans.cbSize = sizeof(pDecryptInfo->trans);
|
|
// pDecryptInfo->trans.hCryptProv = hProv;
|
|
// pDecryptInfo->trans.dwKeySpec = pKPI->dwKeySpec;
|
|
pDecryptInfo->trans.pKeyTrans = pRecipInfo->pKeyTrans;
|
|
pDecryptInfo->trans.dwRecipientIndex = dwRecipientIndex;
|
|
}
|
|
else {
|
|
ReleaseCert(pCertDecrypt);
|
|
pCertDecrypt = NULL;
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// Given the certificate reference, see if we can find it in
|
|
// the passed in certificate stores, if yes then we will attempt
|
|
// to decrypt using that certificate
|
|
//
|
|
// This is a Key Agreement recipient info object. The CAPI 2.0
|
|
// code can deal with both SKI and Issuer/Serial Number references
|
|
//
|
|
// There may be multiple certificate references within a single
|
|
// recipient info object
|
|
//
|
|
|
|
case CMSG_KEY_AGREE_RECIPIENT:
|
|
for (i=0; i<pRecipInfo->pKeyAgree->cRecipientEncryptedKeys; i++) {
|
|
pCertDecrypt = CertFindCertificateInStore(
|
|
hcertstore, X509_ASN_ENCODING, 0,
|
|
CERT_FIND_CERT_ID,
|
|
&pRecipInfo->pKeyAgree->rgpRecipientEncryptedKeys[i]->RecipientId,
|
|
NULL);
|
|
if (pCertDecrypt != NULL) {
|
|
hr = GetCSP(pCertDecrypt, &pDecryptInfo->agree.hCryptProv,
|
|
&pDecryptInfo->agree.dwKeySpec);
|
|
if (SUCCEEDED(hr)) {
|
|
//
|
|
// We find a certificate for this lock box. Setup the
|
|
// structure to be used in decrypting the message.
|
|
//
|
|
|
|
*pdwCtrl = CMSG_CTRL_KEY_AGREE_DECRYPT;
|
|
pDecryptInfo->agree.cbSize = sizeof(pDecryptInfo->agree);
|
|
pDecryptInfo->agree.pKeyAgree = pRecipInfo->pKeyAgree;
|
|
pDecryptInfo->agree.dwRecipientIndex = dwRecipientIndex;
|
|
pDecryptInfo->agree.dwRecipientEncryptedKeyIndex = i;
|
|
|
|
//
|
|
// Need to find the originator information
|
|
//
|
|
|
|
switch(pRecipInfo->pKeyAgree->dwOriginatorChoice) {
|
|
case CMSG_KEY_AGREE_ORIGINATOR_CERT:
|
|
pCertOrig = CertFindCertificateInStore( hcertstore, X509_ASN_ENCODING, 0, CERT_FIND_CERT_ID, &pRecipInfo->pKeyAgree->OriginatorCertId, NULL);
|
|
if (pCertOrig == NULL) {
|
|
hr = S_FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
hr = HrCopyCryptBitBlob(&pCertOrig->pCertInfo->SubjectPublicKeyInfo.PublicKey,
|
|
&pDecryptInfo->agree.OriginatorPublicKey);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
break;
|
|
|
|
case CMSG_KEY_AGREE_ORIGINATOR_PUBLIC_KEY:
|
|
hr = HrCopyCryptBitBlob(&pRecipInfo->pKeyAgree->OriginatorPublicKeyInfo.PublicKey,
|
|
&pDecryptInfo->agree.OriginatorPublicKey);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
hr = NTE_FAIL;
|
|
goto exit;
|
|
}
|
|
break;
|
|
}
|
|
else {
|
|
ReleaseCert(pCertDecrypt);
|
|
pCertDecrypt = NULL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
//
|
|
// We can't find this from a certificate
|
|
//
|
|
|
|
case CMSG_MAIL_LIST_RECIPIENT:
|
|
break;
|
|
|
|
default:
|
|
hr = NTE_FAIL;
|
|
goto exit;
|
|
}
|
|
}
|
|
else {
|
|
CERT_INFO * pCertInfo = (CERT_INFO *) pRecipInfo;
|
|
|
|
for (i=0; i<m_cStores; i++) {
|
|
pCertDecrypt = CertGetSubjectCertificateFromStore(m_rgStores[i],
|
|
X509_ASN_ENCODING, pCertInfo);
|
|
if (pCertDecrypt != NULL) {
|
|
pccdp = (PCMSG_CTRL_DECRYPT_PARA) pDecryptInfo;
|
|
hr = GetCSP(pCertDecrypt, &pccdp->hCryptProv, &pccdp->dwKeySpec);
|
|
if (SUCCEEDED(hr)) {
|
|
//
|
|
// We find a certificate for this lock box. Setup the
|
|
// structure to be used in decrypting the message.
|
|
//
|
|
|
|
*pdwCtrl = CMSG_CTRL_DECRYPT;
|
|
pccdp->cbSize = sizeof(CMSG_CTRL_DECRYPT_PARA);
|
|
pccdp->dwRecipientIndex = dwRecipientIndex;
|
|
}
|
|
else {
|
|
ReleaseCert(pCertDecrypt);
|
|
pCertDecrypt = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we did not find a certificate, then return a failure code
|
|
//
|
|
|
|
if (pCertDecrypt == NULL) {
|
|
hr = S_FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// If we have a certificate, then return it for the user to examine.
|
|
//
|
|
|
|
if (pCertDecrypt != NULL) {
|
|
*ppCertDecrypt = pCertDecrypt;
|
|
pCertDecrypt = NULL;
|
|
}
|
|
|
|
hProv = NULL;
|
|
hr = S_OK;
|
|
exit:
|
|
ReleaseCert(pCertDecrypt);
|
|
ReleaseCert(pCertOrig);
|
|
if (hProv != NULL) CryptReleaseContext(hProv, 0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL CCAPIStm::HandleEnveloped()
|
|
{
|
|
DWORD cbData;
|
|
DWORD cCerts;
|
|
DWORD cRecips;
|
|
CMS_CTRL_DECRYPT_INFO decryptInfo = {0};
|
|
DWORD dexRecip;
|
|
DWORD dwCtrl;
|
|
BOOL f;
|
|
BOOL fGotoUser = FALSE;
|
|
HCERTSTORE hcertstore = NULL;
|
|
HCERTSTORE hMsgCertStore = NULL;
|
|
HRESULT hr;
|
|
DWORD i;
|
|
PCCERT_CONTEXT pCertDecrypt = NULL;
|
|
CMSG_CMS_RECIPIENT_INFO * pCmsCertInfo;
|
|
LPVOID pv = NULL;
|
|
|
|
//
|
|
// If we are not suppose to display UI -- return an error about displaying UI now.
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
// 591349 - Compiler Bug For Zero Initialization of Data Structure. Active WinNT 5.1 (Whistler) 1 Server RC1
|
|
// the line above doesn't zero the structure due to this compiler bug
|
|
memset(&decryptInfo, 0, sizeof(decryptInfo));
|
|
|
|
if (m_dwFlagsSEF & SEF_NOUI) {
|
|
return MIME_E_SECURITY_UIREQUIRED;
|
|
}
|
|
|
|
//
|
|
// this call exists for one and only one purpose. We must
|
|
// be sure that we have read and parsed all of the RecipientInfo structures
|
|
// before we start processing them. Since the algorithm parameter is after
|
|
// the last of the last of the recipient structures, this make sure of that.
|
|
//
|
|
|
|
pv = PVGetMsgParam(m_hMsg, CMSG_ENVELOPE_ALGORITHM_PARAM, 0, NULL);
|
|
if (pv == NULL) {
|
|
goto gle;
|
|
}
|
|
MemFree(pv); pv = NULL;
|
|
|
|
//
|
|
// Fetch the set of certificates on the message object
|
|
//
|
|
|
|
cbData = sizeof(cCerts);
|
|
f = CryptMsgGetParam(m_hMsg, CMSG_CERT_COUNT_PARAM, 0, &cCerts, &cbData);
|
|
Assert(f);
|
|
|
|
if (f && (cCerts > 0)) {
|
|
// since there are certs included, let's try them first when matching
|
|
// certs with enryptors.
|
|
|
|
// get the store set
|
|
// make sure we keep hold of our provider
|
|
hMsgCertStore = CertOpenStore(CERT_STORE_PROV_MSG, X509_ASN_ENCODING,
|
|
m_hProv, 0, m_hMsg);
|
|
if (hMsgCertStore) {
|
|
m_dwFlagsStm |= CSTM_DONTRELEASEPROV; // given unto the store
|
|
}
|
|
|
|
// if it failed, we just don't have a store then
|
|
Assert(hMsgCertStore != NULL);
|
|
m_psldData->m_hstoreEncrypt = CertDuplicateStore(hMsgCertStore);
|
|
}
|
|
|
|
//
|
|
// Retrieve the count of recipient infos on the message
|
|
//
|
|
|
|
cbData = sizeof(cRecips);
|
|
if (!CryptMsgGetParam(m_hMsg, g_FSupportV3 ? CMSG_CMS_RECIPIENT_COUNT_PARAM :
|
|
CMSG_RECIPIENT_COUNT_PARAM, 0, &cRecips, &cbData)) {
|
|
goto gle;
|
|
}
|
|
|
|
//
|
|
// If we were provided an actual certificate, see if this is it...
|
|
// We will either search for the provided certificate or in the provided
|
|
// certificate stores, but not both.
|
|
//
|
|
|
|
if (m_pUserCertDecrypt != NULL) {
|
|
hcertstore = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING,
|
|
NULL, 0, NULL);
|
|
if (hcertstore == NULL) {
|
|
hr = HrGetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
if (!CertAddCertificateContextToStore(hcertstore, m_pUserCertDecrypt,
|
|
CERT_STORE_ADD_ALWAYS, NULL)) {
|
|
hr = HrGetLastError();
|
|
goto exit;
|
|
}
|
|
}
|
|
else {
|
|
if (g_FSupportV3) {
|
|
hcertstore = CertOpenStore(CERT_STORE_PROV_COLLECTION, X509_ASN_ENCODING,
|
|
NULL, 0, NULL);
|
|
if (hcertstore == NULL) {
|
|
hr = HrGetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
for (i=0; i<m_cStores; i++) {
|
|
CertAddStoreToCollection(hcertstore, m_rgStores[i], 0, 0);
|
|
}
|
|
|
|
if (hMsgCertStore != NULL) {
|
|
CertAddStoreToCollection(hcertstore, hMsgCertStore, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// For each possible recipient
|
|
tryAgain:
|
|
for (dexRecip=0; dexRecip<cRecips; dexRecip++) {
|
|
//
|
|
// Retrieve the desciption of the i-th recipient's lockbox
|
|
//
|
|
|
|
hr = HrGetMsgParam(m_hMsg, g_FSupportV3 ? CMSG_CMS_RECIPIENT_INFO_PARAM :
|
|
CMSG_RECIPIENT_INFO_PARAM, dexRecip, (LPVOID *) &pv, NULL);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Look to see if there is a decrypt item we can fill in here.
|
|
//
|
|
|
|
if (fGotoUser) {
|
|
//
|
|
// Look to see if there is a decrypt item that the user can fill in.
|
|
//
|
|
|
|
hr = m_pSmimeCallback->FindKeyFor(m_hwnd, 0, dexRecip,
|
|
(CMSG_CMS_RECIPIENT_INFO *) pv,
|
|
&dwCtrl, &decryptInfo, &pCertDecrypt);
|
|
}
|
|
else {
|
|
hr = FindKeyFor(m_hwnd, 0, dexRecip, (CMSG_CMS_RECIPIENT_INFO *) pv,
|
|
hcertstore, &dwCtrl, &decryptInfo, &pCertDecrypt);
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
if (hr == S_OK) {
|
|
#ifdef SMIME_V3
|
|
if (m_pwszKeyPrompt != NULL) {
|
|
PCMSG_CTRL_DECRYPT_PARA pccdp;
|
|
|
|
switch (dwCtrl) {
|
|
case CMSG_CTRL_KEY_TRANS_DECRYPT:
|
|
CryptSetProvParam(decryptInfo.trans.hCryptProv,
|
|
PP_UI_PROMPT, (LPBYTE) m_pwszKeyPrompt, 0);
|
|
break;
|
|
|
|
case CMSG_CTRL_KEY_AGREE_DECRYPT:
|
|
CryptSetProvParam(decryptInfo.agree.hCryptProv,
|
|
PP_UI_PROMPT, (LPBYTE) m_pwszKeyPrompt, 0);
|
|
break;
|
|
|
|
case CMSG_CTRL_DECRYPT:
|
|
pccdp = (PCMSG_CTRL_DECRYPT_PARA) &decryptInfo;
|
|
CryptSetProvParam(pccdp->hCryptProv,
|
|
PP_UI_PROMPT, (LPBYTE) m_pwszKeyPrompt, 0);
|
|
break;
|
|
}
|
|
}
|
|
#endif // SMIME_V3
|
|
|
|
if (!CryptMsgControl(m_hMsg, CMSG_CRYPT_RELEASE_CONTEXT_FLAG,
|
|
dwCtrl, &decryptInfo)) {
|
|
hr = HrGetLastError();
|
|
|
|
//
|
|
// Force any cleanups in the event of an error
|
|
//
|
|
|
|
switch (dwCtrl) {
|
|
case CMSG_CTRL_KEY_TRANS_DECRYPT:
|
|
CryptReleaseContext(decryptInfo.trans.hCryptProv, 0);
|
|
break;
|
|
|
|
case CMSG_CTRL_KEY_AGREE_DECRYPT:
|
|
CryptReleaseContext(decryptInfo.agree.hCryptProv, 0);
|
|
break;
|
|
|
|
case CMSG_CTRL_MAIL_LIST_DECRYPT:
|
|
Assert(FALSE);
|
|
break;
|
|
|
|
case CMSG_CTRL_DECRYPT:
|
|
CryptReleaseContext(((PCMSG_CTRL_DECRYPT_PARA) &decryptInfo)->hCryptProv, 0);
|
|
break;
|
|
}
|
|
goto exit;
|
|
}
|
|
goto DecryptDone;
|
|
}
|
|
|
|
//
|
|
// Clean up the object returned describing the lock box, if we
|
|
// were unsuccessful in finding a decryption key.
|
|
//
|
|
|
|
MemFree(pv); pv = NULL;
|
|
}
|
|
|
|
//
|
|
// If we are completely unsuccessful and the user has provided
|
|
// us a callback to play with, then give the user a shot at finding
|
|
// the correct decryption parameters.
|
|
//
|
|
|
|
if (!fGotoUser && g_FSupportV3 && (m_pSmimeCallback != NULL)) {
|
|
fGotoUser = TRUE;
|
|
goto tryAgain;
|
|
}
|
|
|
|
CSSDOUT("Could not decrypt the message");
|
|
|
|
m_psldData->m_ulDecValidity = MSV_CANTDECRYPT;
|
|
hr = CS_E_CANT_DECRYPT;
|
|
goto exit;
|
|
|
|
//
|
|
// If we get here, then we
|
|
// 1) found some parameters and
|
|
// 2) the worked.
|
|
//
|
|
|
|
DecryptDone:
|
|
Assert(m_psldData && (m_psldData->m_pccertDecrypt == NULL));
|
|
if (pCertDecrypt != NULL) {
|
|
m_psldData->m_pccertDecrypt = CertDuplicateCertificateContext(pCertDecrypt);
|
|
|
|
// Determine if certificate is expired
|
|
if (0 != CertVerifyTimeValidityWithDelta(NULL, pCertDecrypt->pCertInfo,
|
|
TIME_DELTA_SECONDS)) {
|
|
m_psldData->m_ulDecValidity |= MSV_ENC_FOR_EXPIREDCERT;
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
exit:
|
|
if (pv != NULL) MemFree(pv);
|
|
|
|
CertFreeCertificateContext(pCertDecrypt);
|
|
|
|
if (hMsgCertStore != NULL) {
|
|
CertCloseStore(hMsgCertStore, 0);
|
|
}
|
|
|
|
if (hcertstore != NULL) {
|
|
CertCloseStore(hcertstore, 0);
|
|
}
|
|
|
|
|
|
if (FAILED(hr)) {
|
|
#ifdef DEBUG
|
|
if (NTE_BAD_DATA == hr) {
|
|
CSSDOUT("Could not decrypt. Maybe due to ImportKeyError since");
|
|
CSSDOUT("NTE_BAD_DATA is the result.");
|
|
// If this happens then it is somewhat likely that PKCS2Decrypt
|
|
// failed inside the CSP. (assuming rsabase, rsaenh)
|
|
}
|
|
#endif
|
|
switch (hr) {
|
|
case CS_E_CANT_DECRYPT:
|
|
case CRYPT_E_STREAM_MSG_NOT_READY:
|
|
case HRESULT_FROM_WIN32(ERROR_CANCELLED):
|
|
break;
|
|
|
|
default:
|
|
// I suppose many things could have gone wrong. We thought
|
|
// we had a cert, though, so let's just say the message itself
|
|
// is bogus.
|
|
//N8 this is a bad idea if we are wrapping a signature
|
|
// should be able to tell if the sig failed and display
|
|
// a better error message.
|
|
//N8 CAPI is simply going to return NTE_FAIL b/c they
|
|
// are failing because our callback failed. the
|
|
// innerCAPI should have some failure state in it.
|
|
// Maybe we could use this to set MSV_BADINNERSIG or something.
|
|
// It would be an encryption error (inside that mask)
|
|
//N8 also this is not being used well enough, even for
|
|
// decryption. the secUI should test this bit and
|
|
// say something intelligent about the message. NS does.
|
|
m_psldData->m_ulDecValidity = MSV_INVALID;
|
|
hr = CS_E_MSG_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (CRYPT_E_STREAM_MSG_NOT_READY != hr) {
|
|
return TrapError(hr);
|
|
} else {
|
|
return hr;
|
|
}
|
|
#else
|
|
return hr;
|
|
#endif
|
|
|
|
gle:
|
|
hr = HrGetLastError();
|
|
Assert(S_OK != hr);
|
|
goto exit;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Class-static utility functions
|
|
//
|
|
|
|
HRESULT CCAPIStm::DuplicateSecurityLayerData(const PSECURITY_LAYER_DATA psldIn, PSECURITY_LAYER_DATA *const ppsldOut)
|
|
{
|
|
if (!psldIn || !ppsldOut) {
|
|
return E_POINTER;
|
|
}
|
|
|
|
// Just addref the original and return it
|
|
psldIn->AddRef();
|
|
*ppsldOut = psldIn;
|
|
return(S_OK);
|
|
}
|
|
|
|
void CCAPIStm::FreeSecurityLayerData(PSECURITY_LAYER_DATA psld)
|
|
{
|
|
if (! psld) {
|
|
return;
|
|
}
|
|
|
|
psld->Release();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Statics to file
|
|
//
|
|
|
|
static HRESULT _InitEncodedCert(IN HCERTSTORE hcertstor,
|
|
PCERT_BLOB * prgblobCerts, DWORD * pcCerts,
|
|
PCRL_BLOB * prgblobCrls, DWORD * pcCrl)
|
|
{
|
|
DWORD cbCerts = 0;
|
|
DWORD cbCRLs = 0;
|
|
DWORD cCerts = 0;
|
|
DWORD cCRLs = 0;
|
|
DWORD i;
|
|
LPBYTE pbCert = NULL;
|
|
LPBYTE pbCRL = NULL;
|
|
PCCERT_CONTEXT pccert = NULL;
|
|
PCCRL_CONTEXT pccrl = NULL;
|
|
PCERT_BLOB rgblobCerts = NULL;
|
|
PCRL_BLOB rgblobCRLs = NULL;
|
|
|
|
while ((pccert = CertEnumCertificatesInStore(hcertstor, pccert)) != NULL) {
|
|
cbCerts += LcbAlignLcb(pccert->cbCertEncoded);
|
|
cCerts += 1;
|
|
}
|
|
|
|
while ((pccrl = CertEnumCRLsInStore(hcertstor, pccrl)) != NULL) {
|
|
cbCRLs += LcbAlignLcb(pccrl->cbCrlEncoded);
|
|
cCRLs += 1;
|
|
}
|
|
|
|
if (cCerts > 0) {
|
|
rgblobCerts = (PCERT_BLOB) g_pMoleAlloc->Alloc(LcbAlignLcb(sizeof(CERT_BLOB) * cCerts + cbCerts));
|
|
if (rgblobCerts == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (cCRLs > 0) {
|
|
rgblobCRLs = (PCRL_BLOB) g_pMoleAlloc->Alloc(LcbAlignLcb(sizeof(CRL_BLOB) * cCRLs + cbCRLs));
|
|
if (rgblobCRLs == NULL) {
|
|
g_pMoleAlloc->Free(rgblobCerts);
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
if (cCerts > 0) {
|
|
pbCert = (LPBYTE) &rgblobCerts[cCerts];
|
|
i = 0;
|
|
while ((pccert = CertEnumCertificatesInStore(hcertstor, pccert)) != NULL) {
|
|
memcpy(pbCert, pccert->pbCertEncoded, pccert->cbCertEncoded);
|
|
rgblobCerts[i].pbData = pbCert;
|
|
rgblobCerts[i].cbData = pccert->cbCertEncoded;
|
|
pbCert += LcbAlignLcb(pccert->cbCertEncoded);
|
|
i++;
|
|
}
|
|
Assert(i == cCerts);
|
|
}
|
|
|
|
if (cCRLs > 0) {
|
|
pbCRL = (LPBYTE) &rgblobCRLs[cCRLs];
|
|
i = 0;
|
|
while ((pccrl = CertEnumCRLsInStore(hcertstor, pccrl)) != NULL) {
|
|
memcpy(pbCRL, pccrl->pbCrlEncoded, pccrl->cbCrlEncoded);
|
|
rgblobCRLs[i].pbData = pbCRL;
|
|
rgblobCRLs[i].cbData = pccrl->cbCrlEncoded;
|
|
pbCRL += LcbAlignLcb(pccrl->cbCrlEncoded);
|
|
i++;
|
|
}
|
|
Assert(i == cCRLs);
|
|
}
|
|
|
|
*prgblobCerts = rgblobCerts;
|
|
*pcCerts = cCerts;
|
|
*prgblobCrls = rgblobCRLs;
|
|
*pcCrl = cCRLs;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Ensures that the signer certificates are included in the returned
|
|
// array of blobs.
|
|
static HRESULT _InitEncodedCertIncludingSigners(IN HCERTSTORE hcertstor,
|
|
DWORD cSigners, SignerData rgSigners[],
|
|
PCERT_BLOB * prgblobCerts, DWORD * pcCerts,
|
|
PCRL_BLOB * prgblobCrls, DWORD * pcCrl)
|
|
{
|
|
HRESULT hr;
|
|
HCERTSTORE hCollection = NULL;
|
|
DWORD i;
|
|
|
|
// Loop through signers. Check that they are already included in the
|
|
// certificate store. If not, then, create a collection and memory store
|
|
// to include.
|
|
|
|
for (i = 0; i < cSigners; i++) {
|
|
PCCERT_CONTEXT pSignerCert = rgSigners[i].pccert;
|
|
PCCERT_CONTEXT pStoreCert = NULL;
|
|
|
|
while (NULL != (pStoreCert = CertEnumCertificatesInStore(
|
|
hcertstor, pStoreCert))) {
|
|
if (pSignerCert->cbCertEncoded == pStoreCert->cbCertEncoded &&
|
|
0 == memcmp(pSignerCert->pbCertEncoded,
|
|
pStoreCert->pbCertEncoded,
|
|
pSignerCert->cbCertEncoded))
|
|
break;
|
|
}
|
|
|
|
if (pStoreCert)
|
|
// Signer cert is already included in the store
|
|
CertFreeCertificateContext(pStoreCert);
|
|
else {
|
|
if (NULL == hCollection) {
|
|
// Create collection and memory store to contain the
|
|
// signer certificate
|
|
|
|
HCERTSTORE hMemory = NULL;
|
|
BOOL fResult;
|
|
|
|
hCollection = CertOpenStore(
|
|
CERT_STORE_PROV_COLLECTION,
|
|
X509_ASN_ENCODING,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
if (NULL == hCollection)
|
|
goto CommonReturn;
|
|
|
|
if (!CertAddStoreToCollection(
|
|
hCollection,
|
|
hcertstor,
|
|
0, // dwUpdateFlags
|
|
0 // dwPriority
|
|
))
|
|
goto CommonReturn;
|
|
|
|
hMemory = CertOpenStore(
|
|
CERT_STORE_PROV_MEMORY,
|
|
X509_ASN_ENCODING,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (NULL == hMemory)
|
|
goto CommonReturn;
|
|
|
|
fResult = CertAddStoreToCollection(
|
|
hCollection,
|
|
hMemory,
|
|
CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG,
|
|
1 // dwPriority
|
|
);
|
|
CertCloseStore(hMemory, 0);
|
|
if (!fResult)
|
|
goto CommonReturn;
|
|
|
|
hcertstor = hCollection;
|
|
}
|
|
|
|
CertAddEncodedCertificateToStore(
|
|
hCollection,
|
|
pSignerCert->dwCertEncodingType,
|
|
pSignerCert->pbCertEncoded,
|
|
pSignerCert->cbCertEncoded,
|
|
CERT_STORE_ADD_ALWAYS,
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
CommonReturn:
|
|
hr = _InitEncodedCert(hcertstor, prgblobCerts, pcCerts, prgblobCrls, pcCrl);
|
|
if (hCollection)
|
|
CertCloseStore(hCollection, 0);
|
|
return hr;
|
|
}
|
|
|
|
#ifndef SMIME_V3
|
|
static HRESULT _InitCertInfo(
|
|
IN PCCERT_CONTEXT * rgpCerts,
|
|
IN DWORD cCerts,
|
|
OUT PCERT_INFO ** prgpCertInfo)
|
|
{
|
|
PCERT_INFO* rgpCertInfo = NULL;
|
|
DWORD dwIdx;
|
|
HRESULT hr = S_OK;
|
|
|
|
Assert(prgpCertInfo);
|
|
|
|
if (cCerts) {
|
|
rgpCertInfo = (PCERT_INFO*)g_pMoleAlloc->Alloc(sizeof(CERT_BLOB) * cCerts);
|
|
if (NULL == rgpCertInfo) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
for (dwIdx = 0; dwIdx < cCerts; dwIdx++) {
|
|
rgpCertInfo[dwIdx] = rgpCerts[dwIdx]->pCertInfo;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
*prgpCertInfo = rgpCertInfo;
|
|
return hr;
|
|
}
|
|
#endif // !SMIME_V3
|
|
|
|
void _SMimeCapsFromHMsg(HCRYPTMSG hMsg, DWORD idParam, LPBYTE * ppb, DWORD * pcb)
|
|
{
|
|
DWORD cbData = 0;
|
|
CRYPT_SMIME_CAPABILITY cap;
|
|
CRYPT_SMIME_CAPABILITIES caps;
|
|
BOOL f;
|
|
PCRYPT_ALGORITHM_IDENTIFIER paid = NULL;
|
|
LPBYTE pb = NULL;
|
|
|
|
f = CryptMsgGetParam(hMsg, idParam, 0, NULL, &cbData);
|
|
if ((cbData == 0) || ! MemAlloc((LPVOID *) &paid, cbData)) {
|
|
Assert(FALSE);
|
|
goto error;
|
|
}
|
|
|
|
f = CryptMsgGetParam(hMsg, idParam, 0, paid, &cbData);
|
|
Assert(f);
|
|
|
|
caps.cCapability = 1;
|
|
caps.rgCapability = ∩
|
|
|
|
cap.pszObjId = paid->pszObjId;
|
|
cap.Parameters.cbData = paid->Parameters.cbData;
|
|
cap.Parameters.pbData = paid->Parameters.pbData;
|
|
|
|
cbData = 0;
|
|
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, szOID_RSA_SMIMECapabilities,
|
|
&caps, CRYPT_ENCODE_ALLOC_FLAG, &CryptEncodeAlloc,
|
|
&pb, &cbData)) {
|
|
Assert(FALSE);
|
|
goto error;
|
|
}
|
|
|
|
*ppb = pb;
|
|
*pcb = cbData;
|
|
|
|
exit:
|
|
SafeMemFree(paid);
|
|
return;
|
|
|
|
error:
|
|
*ppb = NULL;
|
|
*pcb = 0;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
#ifdef SMIME_V3
|
|
//// HrBuildContentEncryptionAlg
|
|
//
|
|
// Description:
|
|
// This function is used to decode a smime capability and build the
|
|
// structure we need to pass into the Crypt32 code.
|
|
//
|
|
|
|
HRESULT HrBuildContentEncryptionAlg(PSECURITY_LAYER_DATA psld, BLOB * pblob)
|
|
{
|
|
DWORD cbData;
|
|
HRESULT hr;
|
|
PCRYPT_SMIME_CAPABILITIES pcaps = NULL;
|
|
CMSG_RC2_AUX_INFO * prc2Aux;
|
|
|
|
//
|
|
// Decode the capability which is the bulk encryption algorithm
|
|
//
|
|
|
|
hr = HrDecodeObject(pblob->pBlobData, pblob->cbSize, PKCS_SMIME_CAPABILITIES,
|
|
0, &cbData, (LPVOID *)&pcaps);
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
Assert(pcaps->cCapability == 1);
|
|
DWORD cchSize = (lstrlen(pcaps->rgCapability[0].pszObjId) + 1);
|
|
if (!MemAlloc((LPVOID *) &psld->m_ContentEncryptAlgorithm.pszObjId, cchSize * sizeof(psld->m_ContentEncryptAlgorithm.pszObjId[0])))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
StrCpyN(psld->m_ContentEncryptAlgorithm.pszObjId, pcaps->rgCapability[0].pszObjId, cchSize);
|
|
|
|
//
|
|
// If this is the RC/2 algorithm, then we need to setup the aux info
|
|
// to pass in the algorithm size.
|
|
//
|
|
|
|
if (lstrcmp(pcaps->rgCapability[0].pszObjId, szOID_RSA_RC2CBC) == 0) {
|
|
psld->m_ContentEncryptAlgorithm.Parameters.cbData = 0;
|
|
psld->m_ContentEncryptAlgorithm.Parameters.pbData = NULL;
|
|
|
|
if (!MemAlloc((LPVOID *) &(psld->m_pvEncryptAuxInfo), sizeof(*prc2Aux))) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
prc2Aux = (CMSG_RC2_AUX_INFO *) psld->m_pvEncryptAuxInfo;
|
|
prc2Aux->cbSize = sizeof(*prc2Aux);
|
|
|
|
if (pcaps->rgCapability[0].Parameters.cbData == 0) {
|
|
prc2Aux->dwBitLen = 40;
|
|
}
|
|
else {
|
|
switch(pcaps->rgCapability[0].Parameters.pbData[pcaps->rgCapability[0].Parameters.cbData-1]) {
|
|
case 128:
|
|
case 58:
|
|
prc2Aux->dwBitLen = 128;
|
|
break;
|
|
|
|
case 64:
|
|
case 120:
|
|
prc2Aux->dwBitLen = 64;
|
|
break;
|
|
|
|
case 40:
|
|
case 160:
|
|
default:
|
|
prc2Aux->dwBitLen = 40;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (pcaps->rgCapability[0].Parameters.cbData != 0) {
|
|
if (!MemAlloc((LPVOID *) &psld->m_ContentEncryptAlgorithm.Parameters.pbData,
|
|
pcaps->rgCapability[0].Parameters.cbData)) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
|
|
memcpy(psld->m_ContentEncryptAlgorithm.Parameters.pbData,
|
|
pcaps->rgCapability[0].Parameters.pbData,
|
|
pcaps->rgCapability[0].Parameters.cbData);
|
|
|
|
psld->m_ContentEncryptAlgorithm.Parameters.cbData =
|
|
pcaps->rgCapability[0].Parameters.cbData;
|
|
}
|
|
|
|
|
|
hr = S_OK;
|
|
exit:
|
|
if (pcaps != NULL) MemFree(pcaps);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT HrDeriveKeyWrapAlg(PSECURITY_LAYER_DATA psld, CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO * pAgree)
|
|
{
|
|
LPCSTR pszObjId = psld->m_ContentEncryptAlgorithm.pszObjId;
|
|
|
|
if (lstrcmp(pszObjId, szOID_RSA_RC2CBC) == 0) {
|
|
pAgree->KeyWrapAlgorithm.pszObjId = szOID_RSA_SMIMEalgCMSRC2wrap;
|
|
pAgree->pvKeyWrapAuxInfo = psld->m_pvEncryptAuxInfo;
|
|
}
|
|
else if (lstrcmp(pszObjId, szOID_RSA_DES_EDE3_CBC) == 0) {
|
|
pAgree->KeyWrapAlgorithm.pszObjId = szOID_RSA_SMIMEalgCMS3DESwrap;
|
|
pAgree->pvKeyWrapAuxInfo = NULL;
|
|
}
|
|
else if (lstrcmp(pszObjId, szOID_INFOSEC_mosaicConfidentiality) == 0) {
|
|
pAgree->KeyWrapAlgorithm.pszObjId = "2.16.840.1.101.2.1.1.24";
|
|
pAgree->pvKeyWrapAuxInfo = NULL;
|
|
}
|
|
else {
|
|
return NTE_NOT_FOUND;
|
|
}
|
|
return S_OK;
|
|
}
|
|
#endif // SMIME_V3
|
|
|
|
|
|
|
|
#ifdef SMIME_V3
|
|
|
|
|
|
//
|
|
// Read in admin option that determines whether a msg with disparate
|
|
// Labels is shown or not.
|
|
//
|
|
BOOL FHideMsgWithDifferentLabels()
|
|
{
|
|
DWORD cbData = 0;
|
|
DWORD dwType = 0;
|
|
DWORD dwValue = 0;
|
|
BOOL fHideMsg = FALSE;
|
|
HKEY hkey = NULL;
|
|
LONG lRes;
|
|
|
|
// Open the security label admin defaults key.
|
|
lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szSecLabelAdminRegKey, 0,
|
|
KEY_READ, &hkey);
|
|
if ( (ERROR_SUCCESS != lRes) || (NULL == hkey) ) {
|
|
// No admin label options were found.
|
|
goto exit;
|
|
}
|
|
|
|
cbData = sizeof(dwValue);
|
|
lRes = RegQueryValueEx(hkey, c_szHideMsgWithDifferentLabels, NULL,
|
|
&dwType, (LPBYTE) &dwValue, &cbData);
|
|
if (ERROR_SUCCESS != lRes) {
|
|
goto exit;
|
|
}
|
|
|
|
if (0x01 == dwValue) {
|
|
fHideMsg = TRUE;
|
|
}
|
|
|
|
exit:
|
|
if (NULL != hkey) RegCloseKey(hkey);
|
|
return fHideMsg;
|
|
}
|
|
|
|
//
|
|
// Read in admin option that determines how to process a label in a
|
|
// signture with errors.
|
|
// Returns 0, 1, 2 for ProcessAnyway, Grant, Deny(default).
|
|
//
|
|
DWORD DwProcessLabelWithCertError()
|
|
{
|
|
DWORD cbData = 0;
|
|
DWORD dwType = 0;
|
|
DWORD dwValue = CertErrorProcessLabelDeny;
|
|
BOOL dwProcessMsg = 0;
|
|
HKEY hkey = NULL;
|
|
LONG lRes;
|
|
|
|
// Open the security label admin defaults key.
|
|
lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szSecLabelAdminRegKey, 0,
|
|
KEY_READ, &hkey);
|
|
if ( (ERROR_SUCCESS != lRes) || (NULL == hkey) ) {
|
|
// No admin label options were found.
|
|
goto exit;
|
|
}
|
|
|
|
// Read in the admin option.
|
|
cbData = sizeof(dwValue);
|
|
lRes = RegQueryValueEx(hkey, c_szCertErrorWithLabel, NULL,
|
|
&dwType, (LPBYTE) &dwValue, &cbData);
|
|
if (ERROR_SUCCESS != lRes) {
|
|
dwValue = CertErrorProcessLabelDeny;
|
|
goto exit;
|
|
}
|
|
|
|
// If the value isn't one of the known ones, force it to the default value.
|
|
if ( (CertErrorProcessLabelAnyway != dwValue) && (CertErrorProcessLabelGrant != dwValue) &&
|
|
(CertErrorProcessLabelDeny != dwValue) ) {
|
|
dwValue = CertErrorProcessLabelDeny;
|
|
}
|
|
|
|
exit:
|
|
if (NULL != hkey) RegCloseKey(hkey);
|
|
return dwValue;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Given a label, queries the policy whether access is to be granted.
|
|
// (If reqd policy doesn't exist, it also tries to query the default
|
|
// policy, if one exists).
|
|
//
|
|
HRESULT HrCheckLabelAccess(const DWORD dwFlags, const HWND hwnd,
|
|
PSMIME_SECURITY_LABEL plabel, const PCCERT_CONTEXT pccertDecrypt,
|
|
const PCCERT_CONTEXT pccertSigner, const HCERTSTORE hcertstor)
|
|
{
|
|
HRESULT hr = MIME_E_SECURITY_LABELACCESSDENIED;
|
|
|
|
HKEY hkey = NULL;
|
|
HKEY hkeySub = NULL;
|
|
HINSTANCE hinstDll = NULL;
|
|
PFNGetSMimePolicy pfnGetSMimePolicy = NULL;
|
|
ISMimePolicyCheckAccess *pspca = NULL;
|
|
LONG lRes;
|
|
DWORD dwType;
|
|
DWORD cbData;
|
|
CHAR szDllPath[MAX_PATH];
|
|
CHAR szExpandedDllPath[MAX_PATH];
|
|
CHAR szFuncName[MAX_FUNC_NAME];
|
|
|
|
|
|
if ((NULL == plabel) || (NULL == plabel->pszObjIdSecurityPolicy)) {
|
|
hr = S_OK; // No label/policyoid => access granted.
|
|
goto exit;
|
|
}
|
|
|
|
|
|
|
|
// Open the security policies key.
|
|
lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szSecLabelPoliciesRegKey, 0,
|
|
KEY_READ, &hkey);
|
|
if ( (ERROR_SUCCESS != lRes) || (NULL == hkey) ) {
|
|
// No security policies are registered. Deny access.
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// Open the security policy (or default policy regkey).
|
|
lRes = RegOpenKeyEx(hkey, plabel->pszObjIdSecurityPolicy, 0, KEY_READ, &hkeySub);
|
|
if ((ERROR_SUCCESS != lRes) || (NULL == hkeySub)) {
|
|
if (hkeySub != NULL) {
|
|
RegCloseKey(hkeySub);
|
|
hkeySub = NULL;
|
|
}
|
|
|
|
// Try opening the default policy, if one exists.
|
|
lRes = RegOpenKeyEx(hkey, c_szDefaultPolicyOid, 0, KEY_READ, &hkeySub);
|
|
if ((ERROR_SUCCESS != lRes) || (NULL == hkeySub)) {
|
|
// couldn't find specified_and_default policy. deny access.
|
|
goto ErrorReturn;
|
|
}
|
|
}
|
|
|
|
Assert(NULL != hkeySub);
|
|
// get the path to the policy dll, and load it.
|
|
cbData = sizeof(szDllPath);
|
|
lRes = RegQueryValueEx(hkeySub, c_szSecurityPolicyDllPath, NULL,
|
|
&dwType, (LPBYTE)szDllPath, &cbData);
|
|
if (ERROR_SUCCESS != lRes) {
|
|
// policy not correctly registered. deny access.
|
|
goto ErrorReturn;
|
|
}
|
|
szDllPath[ ARRAYSIZE(szDllPath) - 1 ] = '\0';
|
|
// expand environment strings (if any) in the dll path we read in.
|
|
if (REG_EXPAND_SZ == dwType)
|
|
{
|
|
ZeroMemory(szExpandedDllPath, ARRAYSIZE(szExpandedDllPath));
|
|
ExpandEnvironmentStrings(szDllPath, szExpandedDllPath, ARRAYSIZE(szExpandedDllPath));
|
|
szExpandedDllPath[ARRAYSIZE(szExpandedDllPath) - 1] = '\0';
|
|
StrCpyN(szDllPath, szExpandedDllPath, ARRAYSIZE(szDllPath));
|
|
}
|
|
|
|
hinstDll = LoadLibraryEx(szDllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
if (NULL == hinstDll) {
|
|
// couldn't load policy. deny access.
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// get the entry func name.
|
|
cbData = sizeof(szFuncName);
|
|
lRes = RegQueryValueEx(hkeySub, c_szSecurityPolicyFuncName, NULL,
|
|
&dwType, (LPBYTE)szFuncName, &cbData);
|
|
if (ERROR_SUCCESS != lRes) {
|
|
// policy not correctly registered. deny access.
|
|
goto ErrorReturn;
|
|
}
|
|
pfnGetSMimePolicy = (PFNGetSMimePolicy) GetProcAddress(hinstDll, szFuncName);
|
|
if (NULL == pfnGetSMimePolicy) {
|
|
// couldn't get proc address. deny access.
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
|
|
hr = (pfnGetSMimePolicy) (0, plabel->pszObjIdSecurityPolicy, GetACP(),
|
|
IID_ISMimePolicyCheckAccess, (LPUNKNOWN *) &pspca);
|
|
if (FAILED(hr) || (NULL == pspca)) {
|
|
// couldn't get required interface,
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// Call into the policy module to find out if access is to be denied/granted.
|
|
hr = pspca->IsAccessGranted(dwFlags, hwnd, plabel, pccertDecrypt,
|
|
pccertSigner, hcertstor);
|
|
|
|
|
|
// fall through to exit.
|
|
|
|
|
|
|
|
exit:
|
|
if (pspca) pspca->Release();
|
|
if (hinstDll) FreeLibrary(hinstDll);
|
|
if (hkeySub) RegCloseKey(hkeySub);
|
|
if (hkey) RegCloseKey(hkey);
|
|
|
|
return hr;
|
|
|
|
ErrorReturn:
|
|
if (! FAILED(hr)) {
|
|
// If we had an error, but didn't get a failure code, force a failure.
|
|
hr |= 0x80000000;
|
|
}
|
|
goto exit;
|
|
}
|
|
#endif // SMIME_V3
|
|
|
|
|
|
|
|
/* * * END --- CAPISTM.CPP --- END * * */
|