Windows2003-3790/inetcore/outlookexpress/inetcomm/mimeole/smime.cpp
2020-09-30 16:53:55 +02:00

4608 lines
152 KiB
C++

/*
** s m i m e . c p p
**
** Purpose:
** Implementation of a class to wrap around CAPI functionality
**
** History
** 1/26/98 (brucek) Allow multiple security layers (triple-wrapping)
** 6/15/97: (t-erikne) CAPI streaming
** 5/18/97: (t-erikne) new IMimeSecurity interface
** 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 "smime.h"
#include "vstream.h"
#include "olealloc.h"
#include "capistm.h"
#include "bookbody.h"
#ifndef MAC
#include <shlwapi.h>
#endif // !MAC
#include <demand.h>
// from dllmain.h
extern CMimeAllocator * g_pMoleAlloc;
extern CRITICAL_SECTION g_csDllMain;
extern ULONG DllAddRef(void);
extern ULONG DllRelease(void);
extern HCERTSTORE WINAPI OpenCachedMyStore();
extern HCERTSTORE WINAPI OpenCachedAddressBookStore();
#define MST_THIS_SIGN_ENCRYPT (MST_THIS_SIGN | MST_THIS_ENCRYPT)
///////////////////////////////////////////////////////////////////////////
//
// Static Prototypes
//
static void _FreeCertArray(PCCERT_CONTEXT *rgpCert, const UINT cCerts);
static HRESULT _HrConvertHrFromGetCertToEncode(HRESULT hr, const BOOL fEncrypt);
#ifndef SMIME_V3
static HRESULT ConstructAuthAttributes(BLOB * pblEncoded, BLOB * pblAuthAttr, FILETIME * pftSigntime, BLOB * pblSymcaps);
#endif // SMIME_V3
extern HRESULT HrCopyBlob(LPCBLOB pIn, LPBLOB pOut);
static LPBYTE DuplicateMemory(LPBYTE lpvIn, ULONG cbIn);
///////////////////////////////////////////////////////////////////////////
//
// Macros
//
#define CHECKSMIMEINITDW { if (FAILED(CheckInit())) return (DWORD)-1; }
#define CHECKSMIMEINITV { if (FAILED(CheckInit())) return; }
#define CHECKSMIMEINITB { if (FAILED(CheckInit())) return FALSE; }
#define CHECKSMIMEINIT { if (FAILED(CheckInit())) return MIME_E_SECURITY_NOTINIT; }
#define SCHECKSMIMEINITP { if (FAILED(StaticCheckInit())) return NULL; }
#define SCHECKSMIMEINITV { if (FAILED(StaticCheckInit())) return; }
#define SCHECKSMIMEINIT { if (FAILED(StaticCheckInit())) return MIME_E_SECURITY_NOTINIT; }
#define ALLOCED(_pv) \
(0 != g_pMalloc->DidAlloc(_pv))
#define THIS_AS_UNK ((IUnknown *)(IStream *)this)
///////////////////////////////////////////////////////////////////////////
//
// Globals
//
ASSERTDATA
static const char s_szSMIMEP7s[] = "smime.p7s";
static const char s_szSMIMEP7m[] = "smime.p7m";
#ifdef DEBUG
static LPCSTR s_lpszCertStore = "c:\\ttfn\\debug.sto";
// emit signatures, encryption that should be broken
static BOOL s_fDebugEmitBroken = 0;
// show the certificate found by HrGetUsableCert
static BOOL s_fDebugShowFoundCert = 0;
// copy the message source to a BYTE *
static BOOL s_fDebugDumpWholeMsg = 1;
// show/select certs w/o email oids
static BOOL s_fDebugAllowNoEmail = 1;
#endif // DEBUG
static const char s_cszMy[] = "My";
static const char s_cszWABCertStore[] = "AddressBook";
CRYPT_ENCODE_PARA CryptEncodeAlloc = {
sizeof(CRYPT_ENCODE_PARA), CryptAllocFunc, CryptFreeFunc
};
CRYPT_DECODE_PARA CryptDecodeAlloc = {
sizeof(CRYPT_DECODE_PARA), CryptAllocFunc, CryptFreeFunc
};
///////////////////////////////////////////////////////////////////////////
//
// Initialization of statics to class
//
#ifdef MAC
EXTERN_C WINCRYPT32API HCERTSTORE WINAPI MacCertOpenStore(LPCSTR lpszStoreProvider,
DWORD dwEncodingType,
HCRYPTPROV hCryptProv,
DWORD dwFlags,
const void *pvPara);
#define CertOpenStore MacCertOpenStore
// We don't have DLL's on the Mac, so let's just hardcode the vtable.
CAPIfuncs CSMime::ms_CAPI = { CertEnumCertificatesInStore,
CertNameToStrA
};
#endif // MAC
///////////////////////////////////////////////////////////////////////////
//
// inlines
//
INLINE void ReleaseCert(PCCERT_CONTEXT pc)
{ if (pc) CertFreeCertificateContext(pc); }
INLINE void ReleaseCertStore(HCERTSTORE hc)
{ if (hc) CertCloseStore(hc, 0); }
INLINE void FreeCert(PCCERT_CONTEXT pc)
{ CertFreeCertificateContext(pc); }
INLINE PCCERT_CONTEXT DupCert(const PCCERT_CONTEXT pc)
{ return CertDuplicateCertificateContext(pc); }
///////////////////////////////////////////////////////////////////////////
//
// ctor, dtor
//
CSMime::CSMime(void)
{
DllAddRef();
m_cRef = 1;
InitializeCriticalSection(&m_cs);
DOUT("CSMIME::constructor() %#x -> %d", this, m_cRef);
}
CSMime::~CSMime()
{
DOUT("CSMIME::destructor() %#x -> %d", this, m_cRef);
DeleteCriticalSection(&m_cs);
DllRelease();
}
///////////////////////////////////////////////////////////////////////////
//
// IUnknown methods
//
STDMETHODIMP CSMime::QueryInterface(REFIID riid, LPVOID *ppv)
{
if (!ppv)
return TrapError(E_INVALIDARG);
// Find IID
if (IID_IUnknown == riid)
*ppv = (IUnknown *)(IMimeSecurity *)this;
else if (IID_IMimeSecurity == riid)
*ppv = (IMimeSecurity *)this;
else
{
*ppv = NULL;
return TrapError(E_NOINTERFACE);
}
((IUnknown *)*ppv)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) CSMime::AddRef(void)
{
DOUT("CSMime::AddRef() %#x -> %d", this, m_cRef+1);
InterlockedIncrement((LPLONG)&m_cRef);
return m_cRef;
}
STDMETHODIMP_(ULONG) CSMime::Release(void)
{
DOUT("CSMime::Release() %#x -> %d", this, m_cRef-1);
if (0 == InterlockedDecrement((LPLONG)&m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
///////////////////////////////////////////////////////////////////////////
//
// Initialization functions
//
STDMETHODIMP CSMime::CheckInit(void)
{
register BOOL f;
EnterCriticalSection(&m_cs);
#ifdef MAC
f = TRUE;
#else // !MAC
f = !!DemandLoadCrypt32();
#endif // MAC
#ifdef DEBUG
if(!f) {
DebugStrf("CSMime not initialized ! ! !\n");
}
#endif
LeaveCriticalSection(&m_cs);
return f ? S_OK : MIME_E_SECURITY_NOTINIT;
}
inline HRESULT CSMime::StaticCheckInit(void)
{
#ifdef MAC
return S_OK;
#else // !MAC
HRESULT hr;
if(DemandLoadCrypt32()) {
hr = S_OK;
}
else {
#ifdef DEBUG
DebugStrf("CSMime not initialized ! ! !\n");
#endif
hr = MIME_E_SECURITY_NOTINIT;
}
return hr;
#endif // MAC
}
/* InitNew:
**
** Purpose:
** Called after the ctor by clients. Initializes CSMime.
*/
STDMETHODIMP CSMime::InitNew(void)
{
register HRESULT hr;
EnterCriticalSection(&m_cs);
hr = HrInitCAPI();
#ifdef DEBUG
if (SUCCEEDED(hr))
#ifdef MAC
InitDebugHelpers((HINSTANCE) 1);
#else // !MAC
InitDebugHelpers(/*g_hCryptoDll*/ (HINSTANCE) 1);
#endif // MAC
if (0) {
DumpAlgorithms();
}
TrapError(hr);
#endif
LeaveCriticalSection(&m_cs);
if (E_FAIL == hr) {
hr = MIME_E_SECURITY_NOTINIT;
}
return hr;
}
/* InitCAPI:
**
** Purpose:
** Loads the required dll and inits the function table.
** Returns:
** MIME_E_SECURITY_LOADCRYPT32 if LoadLibrary fails
** MIME_E_SECURITY_BADPROCADDR if any of the GetProcAddress calls fail
*/
HRESULT CSMime::HrInitCAPI()
{
#ifdef MAC
return S_OK;
#else // !MAC
HRESULT hr = S_OK;
UINT u = 0;
FARPROC *pVTable;
EnterCriticalSection(&g_csDllMain);
if (!DemandLoadCrypt32()) {
hr = TrapError(MIME_E_SECURITY_LOADCRYPT32);
goto ErrorReturn;
}
exit:
LeaveCriticalSection(&g_csDllMain);
return hr;
ErrorReturn:
{
DWORD dwErr = GetLastError();
UnloadCAPI();
SetLastError(dwErr);
Assert(S_OK != hr);
}
goto exit;
#endif // MAC
}
/* UnloadAll:
**
** Purpose:
** Called during deinit of our DLL to unload S/MIME
*/
void CSMime::UnloadAll(void)
{
UnloadCAPI();
return;
}
/* UnloadCAPI:
**
** Purpose:
** Frees the crypt32 library. Note that this will
** cause subsequent CheckInit calls to fail
*/
void CSMime::UnloadCAPI()
{
}
///////////////////////////////////////////////////////////////////////////
//
// Encode/Decode stuff . . .
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Message crackers
//
#ifndef WIN16
HRESULT CSMime::GetMessageType(
HWND hwndParent,
IMimeBody *const pBody,
DWORD *const pdwSecType)
#else
HRESULT CSMime::GetMessageType(
const HWND hwndParent,
IMimeBody *const pBody,
DWORD *const pdwSecType)
#endif // !WIN16
{
return StaticGetMessageType(hwndParent, pBody, pdwSecType);
}
/* StaticGetMessageType:
**
** Purpose:
** Used to figure out if a message is signed/encrypted/none/both
** without doing any cryptographic operations.
** Takes:
** IN hwndParent - all modal UI to this
** IN pBody - body to decode
** OUT dwSecType - which, if any, S/MIME types have been applied
** Notes:
** if MIME_E_SECURITY_BADSECURETYPE is returned, pdwSecType is set
** to the actual CMSG_ return from CAPI.
** (7/10/97) MIME_E_SECURITY_BADSECURETYPE now comes from CAPISTM.
*/
HRESULT CSMime::StaticGetMessageType(
HWND hwndParent,
IMimeBody *const pBody,
DWORD *const pdwSecType)
{
HRESULT hr;
// SCHECKSMIMEINIT
if (!(pBody && pdwSecType))
return TrapError(E_INVALIDARG);
CCAPIStm capistmMsg(NULL);
hr = capistmMsg.HrInitialize(0, hwndParent, FALSE, NULL, CSTM_TYPE_ONLY, NULL, NULL);
if (SUCCEEDED(hr)) {
IStream * pstmCapi;
PSECURITY_LAYER_DATA psld;
capistmMsg.QueryInterface(IID_IStream, (void**)&pstmCapi);
#ifdef N_BAD1
// This is what would happen if I were passed a stream
// instead of a body
LPSTREAM pstmBody;
ULARGE_INTEGER uliCopy;
hr = pBody->GetData(IET_BINARY, &pstmBody);
uliCopy.HighPart = (ULONG)-1;
uliCopy.LowPart = (ULONG)-1;
pstmBody->CopyTo(pstmCapi, uliCopy, NULL, NULL);
#else
hr = pBody->GetDataHere(IET_BINARY, pstmCapi);
#endif
// We expect CAPISTM_E_GOTTYPE because the streamer
// fails its Write() as soon as it gets the type
Assert(FAILED(hr));
// However, try to get the data even if we succeeded. That
// would surprise me.
// BUGBUG: Does this make sense? We're only dealing with the outer layer here.
if (psld = capistmMsg.GetSecurityLayerData()) {
if (CAPISTM_E_GOTTYPE == hr) {
hr = S_OK;
}
*pdwSecType = psld->m_dwMsgEnhancement;
psld->Release();
} else {
hr = E_FAIL;
}
pstmCapi->Release();
hr = capistmMsg.EndStreaming();
}
return hr;
}
///////////////////////////////////////////////////////////////////////////
//
// Encode methods
//
/* EncodeMessage:
**
** Purpose:
** call EncodeBody for lazy developers
** Takes:
** IN pTree - the tree of the message
** IN dwFlags - SEF_*
*/
STDMETHODIMP CSMime::EncodeMessage(
IMimeMessageTree *const pTree,
DWORD dwFlags)
{
HRESULT hr;
HBODY hRoot;
if (!pTree || (dwFlags & ~SEF_MASK)) {
hr = TrapError(E_INVALIDARG);
}
else if (SUCCEEDED(hr = pTree->GetBody(IBL_ROOT, NULL, &hRoot))) {
hr = TrapError(EncodeBody(pTree, hRoot, dwFlags|EBF_RECURSE|EBF_COMMITIFDIRTY));
}
return hr;
}
HRESULT CSMime::EncodeMessage2(IMimeMessageTree *const pTree, DWORD dwFlags,
HWND hwnd)
{
HRESULT hr;
HBODY hRoot;
if (!pTree || (dwFlags & ~SEF_MASK)) {
hr = TrapError(E_INVALIDARG);
}
else if (SUCCEEDED(hr = pTree->GetBody(IBL_ROOT, NULL, &hRoot))) {
hr = TrapError(EncodeBody2(pTree, hRoot, dwFlags|EBF_RECURSE|
EBF_COMMITIFDIRTY, hwnd));
}
return hr;
}
/* EncodeBody:
**
** Purpose:
** Do the entirety of the S/MIME encode operation. This includes converting bodyoptions
** to SMIMEINFO and calling the appropriate encoding subfunctions.
**
** Takes:
** IN pTree - the tree of the body to encode
** IN hEncodeRoot - body from which to encode downward
** IN dwFlags - set of EBF_ or SEF_
*/
STDMETHODIMP CSMime::EncodeBody(
IMimeMessageTree *const pTree,
HBODY hEncodeRoot,
DWORD dwFlags)
{
HRESULT hr;
HWND hwnd = NULL;
IMimeBody * pEncodeRoot = NULL;
PROPVARIANT var;
if (! pTree || ! hEncodeRoot) {
hr = TrapError(E_INVALIDARG);
goto exit;
}
// Get the body we are suppose to be encoding
CHECKHR(hr = pTree->BindToObject(hEncodeRoot, IID_IMimeBody, (void**)&pEncodeRoot));
#ifdef _WIN64
if (SUCCEEDED(pEncodeRoot->GetOption(OID_SECURITY_HWND_OWNER_64, &var)) && (NULL != (HWND)(var.pulVal))) {
Assert(VT_UI8 == var.vt);
hwnd = *(HWND *)(&(var.uhVal));
}
#else
if (SUCCEEDED(pEncodeRoot->GetOption(OID_SECURITY_HWND_OWNER, &var)) && (NULL != var.ulVal))
{
Assert(VT_UI4 == var.vt);
hwnd = (HWND)var.ulVal;
}
#endif // _WIN64
hr = EncodeBody2(pTree, hEncodeRoot, dwFlags, hwnd);
exit:
ReleaseObj(pEncodeRoot);
return hr;
}
HRESULT CSMime::EncodeBody2(IMimeMessageTree *const pTree, HBODY hEncodeRoot,
DWORD dwFlags, HWND hwnd)
{
IMimeAddressTable * pAdrTable = NULL;
IMimeEnumAddressTypes * pAdrEnum = NULL;
CVirtualStream * pvstmEncoded = NULL;
IMimeBody * pEncodeRoot = NULL;
HRESULT hr;
SMIMEINFO si;
CERTARRAY caCerts;
BOOL fIgnoreSenderCertProbs;
PSECURITY_LAYER_DATA psldLoop;
PROPVARIANT var;
memset(&si, 0, sizeof(si));
memset(&caCerts, 0, sizeof(caCerts));
if (! pTree || ! hEncodeRoot) {
hr = TrapError(E_INVALIDARG);
goto exit;
}
// Get the body we are suppose to be encoding
CHECKHR(hr = pTree->BindToObject(hEncodeRoot, IID_IMimeBody, (void**)&pEncodeRoot));
// We are going to set the state to don't encode this body
// We we are called this should never be set to TRUE as we are getting explicity called.
// If it is true, then ignore it.
#ifdef DEBUG
hr = pEncodeRoot->GetOption(OID_NOSECURITY_ONSAVE, &var);
Assert((hr == S_OK) && (var.boolVal == FALSE));
#endif // DEBUG
var.boolVal = TRUE;
CHECKHR(hr = pEncodeRoot->SetOption(OID_NOSECURITY_ONSAVE, &var));
hr = OptionsToSMIMEINFO(TRUE, pTree, pEncodeRoot, &si);
if (S_OK != hr) {
goto exit;
}
if (MIME_S_SECURITY_NOOP == hr) {
SMDOUT("Encode body called on plain set.");
goto exit;
}
if (EBF_RECURSE & dwFlags) {
if (MST_DESCENDENT_MASK & si.dwMsgEnhancement) {
AssertSz(0, "nyi: recursion.");
}
}
if (MIME_S_SECURITY_RECURSEONLY == hr) {
hr = MIME_S_SECURITY_NOOP;
SMDOUT("Encode body called on plain body.");
goto exit;
}
fIgnoreSenderCertProbs = (SEF_ENCRYPTWITHNOSENDERCERT|SEF_SENDERSCERTPROVIDED) & dwFlags;
psldLoop = si.psldLayers;
while (psldLoop) {
// Can only apply one of signing or encryption
Assert(!!(psldLoop->m_dwMsgEnhancement & MST_THIS_SIGN) +
!!(psldLoop->m_dwMsgEnhancement & MST_THIS_ENCRYPT) == 1);
if (psldLoop->m_dwMsgEnhancement & MST_SIGN_MASK) {
// 2nd attempt at finding a signing cert
Assert(psldLoop->m_rgSigners != NULL);
if (psldLoop->m_rgSigners[0].pccert == NULL) {
CHECKHR(hr = HrGetNeededAddresses(IAT_FROM, pTree, &pAdrTable,
&pAdrEnum));
hr = HrGetCertificates(pAdrTable, pAdrEnum, ITT_SIGNING,
fIgnoreSenderCertProbs, &caCerts);
if (S_OK != hr) {
hr = _HrConvertHrFromGetCertToEncode(hr, FALSE);
Assert(FAILED(hr));
goto exit;
}
Assert(caCerts.rgpCerts);
Assert(1 == caCerts.cCerts);
// need the sender's cert listed a second time
if (caCerts.rgpCerts[0]) {
psldLoop->m_rgSigners[0].pccert = DupCert(caCerts.rgpCerts[0]);
}
}
}
if (psldLoop->m_dwMsgEnhancement & MST_THIS_ENCRYPT) {
Assert(! caCerts.rgpCerts);
// 2nd place to look for encryption certs
ReleaseObj(pAdrTable);
ReleaseObj(pAdrEnum);
// NOTE: Do NOT include IAT_REPLYTO in the needed addresses!
#ifdef SMIME_V3
if (psldLoop->m_rgRecipientInfo == NULL) {
hr = E_FAIL;
Assert(FAILED(hr));
goto exit;
}
#else // !SMIME_V3
if (psldLoop->m_rgEncryptItems == NULL) {
hr = HrGetNeededAddresses(IAT_FROM | IAT_TO | IAT_CC | IAT_BCC | IAT_SENDER, pTree, &pAdrTable, &pAdrEnum);
if (SUCCEEDED(hr)) {
DWORD i;
DWORD dexBogus; // the index into certResults of the NULL sender's cert
hr = HrGetCertificates(pAdrTable, pAdrEnum, ITT_ENCRYPTION,
fIgnoreSenderCertProbs, &caCerts);
//
// Outlook98 doesn't pass us an address table, all of the certs
// are already in the SMIMEINFO struct. So if we're told that the
// sender certs are provided, and we don't have any certificates
// from the table, we just continue. We'll fail properly
// in the zero certificate case down below.
//
if ((S_OK != hr) &&
!((MIME_S_SECURITY_NOOP == hr) &&
((fIgnoreSenderCertProbs & SEF_SENDERSCERTPROVIDED) != 0))) {
hr = _HrConvertHrFromGetCertToEncode(hr, TRUE);
Assert(FAILED(hr));
goto exit;
}
if (caCerts.cCerts == 0) {
hr = TrapError(MIME_E_SECURITY_NOCERT);
goto exit;
}
// just reference the certResults as the encryption array
Assert(0 == psldLoop->m_cEncryptItems);
if (!MemAlloc((LPVOID *) &psldLoop->m_rgEncryptItems,
sizeof(EncryptItem))) {
hr = E_OUTOFMEMORY;
goto exit;
}
psldLoop->m_cEncryptItems = 1;
psldLoop->m_rgEncryptItems[0].dwTagType = ENCRYPT_ITEM_TRANSPORT;
psldLoop->m_rgEncryptItems[0].Transport.cCert = caCerts.cCerts;
psldLoop->m_rgEncryptItems[0].Transport.rgpccert = caCerts.rgpCerts;
psldLoop->m_rgEncryptItems[0].Transport.blobAlg.pBlobData = NULL;
psldLoop->m_rgEncryptItems[0].Transport.blobAlg.cbSize = 0;
caCerts.rgpCerts = NULL; // we're holding it in the layer now
// Encryption algorithm
if (SUCCEEDED(pEncodeRoot->GetOption(OID_SECURITY_ALG_BULK, &var)) &&
(0 != var.blob.cbSize)) {
Assert(VT_BLOB == var.vt);
psldLoop->m_rgEncryptItems[0].Transport.blobAlg.pBlobData = var.blob.pBlobData;
psldLoop->m_rgEncryptItems[0].Transport.blobAlg.cbSize = var.blob.cbSize;
}
}
}
else {
if (!fIgnoreSenderCertProbs) {
hr = TrapError(MIME_E_SECURITY_ENCRYPTNOSENDERCERT);
goto exit;
}
}
#endif // SMIME_V3
} // encryption
// Clean up cert array for next round
if (caCerts.rgpCerts) {
_FreeCertArray(caCerts.rgpCerts, caCerts.cCerts);
caCerts.rgpCerts = NULL;
}
psldLoop = psldLoop->m_psldInner;
}
if (!(pvstmEncoded = new CVirtualStream)) {
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
if (FClearSign(si.dwMsgEnhancement)) {
if (FEncrypt(si.dwMsgEnhancement)) {
si.dwMsgEnhancement |= MST_BLOB_FLAG;
CHECKHR(hr = HrEncodeOpaque(&si, pTree, hEncodeRoot, pEncodeRoot, pvstmEncoded, hwnd));
}
else {
CHECKHR(hr = HrEncodeClearSigned(&si, pTree, hEncodeRoot, pEncodeRoot,
pvstmEncoded, (dwFlags & EBF_COMMITIFDIRTY), hwnd));
// Since this went multi-part, the root body changed on us and we need to get
// it back
pEncodeRoot->Release();
CHECKHR(hr = pTree->BindToObject(hEncodeRoot, IID_IMimeBody,
(void**)&pEncodeRoot));
}
}
else {
// encryption and/or signedData signature
CHECKHR(hr = HrEncodeOpaque(&si, pTree, hEncodeRoot, pEncodeRoot, pvstmEncoded, hwnd));
}
CommonReturn:
if (pEncodeRoot != NULL)
{
var.boolVal = FALSE;
pEncodeRoot->SetOption(OID_NOSECURITY_ONSAVE, &var);
}
ReleaseObj(pAdrTable);
ReleaseObj(pAdrEnum);
ReleaseObj(pvstmEncoded);
ReleaseObj(pEncodeRoot);
FreeSMIMEINFO(&si);
return hr;
exit:
// these objects are only need attention on error
_FreeCertArray(caCerts.rgpCerts, caCerts.cCerts);
goto CommonReturn;
}
/* HrEncodeClearSigned:
**
** Purpose:
** Builds the multipart message needed for clear signing and
** retrieves the data stream to pass to the stream wrapepr.
** Takes:
** IN psi - needs the certificate array, signing
** certificate, etc.
** IN pTree - tree containing body to convert to m/s
** IN pEncodeRoot - body that will become 1st child of the m/s
** OUT lpstmOut - contains the signature bits (PKCS#7 nodata)
** Returns:
** hresult. no function-specific return values.
*/
HRESULT CSMime::HrEncodeClearSigned(
SMIMEINFO *const psi,
IMimeMessageTree *const pTree,
const HBODY hEncodeRoot,
IMimeBody *const pEncodeRoot,
LPSTREAM lpstmOut,
BOOL fCommit,
HWND hwnd)
{
HBODY hNew, hSignature, hData;
HRESULT hr;
HRESULT hr_smime = S_OK;
DWORD i;
IMimeBody * pSig = NULL;
IMimeBody * pMS = NULL;
IMimeBody * pData = NULL;
IMimeBody * pRoot = NULL;
IStream * pstmMsg = NULL;
BODYOFFSETS boData;
LARGE_INTEGER liPos;
PROPVARIANT var;
#ifdef DEBUG
BLOB blobMsg = {NULL,0};
#endif
ULARGE_INTEGER uliCopy;
ALG_ID aid;
const char * lpszProtocol;
CCAPIStm capistmMsg(lpstmOut);
// We need lpstmOut because it is the media of transmission for
// the signature. The sig body gets it through SetData
Assert(psi && hEncodeRoot && pEncodeRoot && pTree && lpstmOut);
CHECKHR(hr = pTree->ToMultipart(hEncodeRoot, STR_SUB_SIGNED, &hNew));
if (fCommit) {
BOOL fCleanup;
// Need to commit the tree, but if Tonja cleans it, I'll lose my
// multipart. So, turn off cleaning and save the value to set back.
CHECKHR(hr = pTree->GetOption(OID_CLEANUP_TREE_ON_SAVE, &var));
fCleanup = var.boolVal ? TRUE : FALSE;
var.boolVal = FALSE;
CHECKHR(hr = pTree->SetOption(OID_CLEANUP_TREE_ON_SAVE, &var));
CHECKHR(hr = pTree->Commit(COMMIT_SMIMETRANSFERENCODE));
var.boolVal = (VARIANT_BOOL) !!fCleanup;
pTree->SetOption(OID_CLEANUP_TREE_ON_SAVE, &var);
}
CHECKHR(hr = pTree->GetBody(IBL_FIRST, hNew, &hData));
CHECKHR(hr = pTree->BindToObject(hData, IID_IMimeBody, (LPVOID *)&pData));
CHECKHR(hr = pData->GetOffsets(&boData));
// BUG 38411: I need a clean pristine virginal-white stream
// so we have to go straight to the horse's smelly mouth.
CHECKHR(hr = pTree->GetMessageSource(&pstmMsg, 0));
#if defined(DEBUG) && !defined(MAC)
if (s_fDebugDumpWholeMsg) {
hr = HrStreamToByte(pstmMsg, &blobMsg.pBlobData, &blobMsg.cbSize);
HrRewindStream(pstmMsg);
}
#endif
// Now slice out the part we actually want
liPos.HighPart = 0;
liPos.LowPart = boData.cbHeaderStart;
CHECKHR(hr = pstmMsg->Seek(liPos, STREAM_SEEK_SET, NULL));
CHECKHR(hr = capistmMsg.HrInitialize(0, hwnd, TRUE, psi, CSTM_DETACHED, NULL, psi->psldInner));
if (SUCCEEDED(hr)) {
IStream *pstmCapi;
uliCopy.HighPart = 0;
uliCopy.LowPart = boData.cbBodyEnd-boData.cbHeaderStart;
capistmMsg.QueryInterface(IID_IStream, (void**)&pstmCapi);
hr = pstmMsg->CopyTo(pstmCapi, uliCopy, NULL, NULL);
pstmCapi->Release();
CHECKHR(hr = capistmMsg.EndStreaming());
}
CHECKHR(hr = pTree->InsertBody(IBL_LAST, hNew, &hSignature));
CHECKHR(hr = pTree->BindToObject(hSignature, IID_IMimeBody, (void**)&pSig));
CHECKHR(hr = pSig->SetData(IET_BINARY, STR_CNT_APPLICATION,
STR_SUB_XPKCS7SIG, IID_IStream, (void*)lpstmOut));
if (-1 != psi->ietRequested) {
var.vt = VT_UI4;
var.ulVal = psi->ietRequested;
pSig->SetOption(OID_TRANSMIT_BODY_ENCODING, &var);
}
// Set the properties for the signature blob as in S/MIGv2 3.3
var.vt = VT_LPSTR;
var.pszVal = (LPSTR)STR_DIS_ATTACHMENT;
CHECKHR(hr = pSig->SetProp(PIDTOSTR(PID_HDR_CNTDISP), 0, &var));
var.pszVal = (char *)s_szSMIMEP7s;
pSig->SetProp(PIDTOSTR(PID_PAR_FILENAME), 0, &var);
pSig->SetProp(PIDTOSTR(PID_PAR_NAME), 0, &var);
// Set the parameters on the m/s root as in rfc1847 2.1
CHECKHR(hr = pTree->BindToObject(hNew, IID_IMimeBody, (void**)&pMS));
var.pszVal = (char *)STR_MIME_APPL_PKCS7SIG;
CHECKHR(hr = pMS->SetProp(STR_PAR_PROTOCOL, 0, &var));
// Get the HASH algorithm. Note that we should NOT get a multi-layer
// message with Multipart/Signed so we should not have to worry about
// what layer this is for.
Assert(psi->psldLayers);
Assert(psi->psldLayers->m_psldInner == NULL);
Assert(psi->psldLayers->m_cSigners > 0);
Assert(psi->psldLayers->m_rgSigners != NULL);
if (psi->psldLayers->m_cSigners == 0) {
hr = E_INVALIDARG;
goto exit;
}
lpszProtocol = "unknown";
// M00BUG -- should add these together and remove duplicates
for (i=0; i<psi->psldLayers->m_cSigners; i++) {
hr = MimeOleAlgNameFromSMimeCap(psi->psldLayers->m_rgSigners[i].blobHashAlg.pBlobData,
psi->psldLayers->m_rgSigners[i].blobHashAlg.cbSize,
&lpszProtocol);
}
var.pszVal = (LPSTR)lpszProtocol;
CHECKHR(hr = pMS->SetProp(STR_PAR_MICALG, 0, &var));
var.ulVal = 0;
CHECKHR(hr = pTree->BindToObject(HBODY_ROOT, IID_IMimeBody, (void**)&pRoot));
SideAssert(SUCCEEDED(pRoot->SetOption(OID_SECURITY_SIGNATURE_COUNT, &var)));
SideAssert(SUCCEEDED(pRoot->SetOption(OID_SECURITY_TYPE, &var)));
exit:
#ifdef DEBUG
if (blobMsg.pBlobData) {
MemFree(blobMsg.pBlobData);
}
#endif
ReleaseObj(pRoot);
ReleaseObj(pSig);
ReleaseObj(pData);
ReleaseObj(pMS);
ReleaseObj(pstmMsg);
if (FAILED(hr) && hNew) {
// need to undo the multipartization and delete the sig body
// errors are not as important as the one that has already occured
if (hSignature) {
pTree->DeleteBody(hSignature, 0);
}
pTree->DeleteBody(hNew, DELETE_PROMOTE_CHILDREN);
}
if (S_OK != hr_smime && SUCCEEDED(hr)) {
hr = hr_smime;
}
return hr;
}
/* HrEncodeOpaque:
**
** Purpose:
**
** Takes:
** IN psi - needs signing cert,
** certificate array (opt)
** IN pTree - the normal tree baggage
** IN hEncodeRoot - the tree likes handles
** IN pEncodeRoot - body to begin encoding at
** OUT lpstmOut - contains the PKCS#7 message
*/
HRESULT CSMime::HrEncodeOpaque(
SMIMEINFO *const psi,
IMimeMessageTree * pTree,
HBODY hEncodeRoot,
IMimeBody * pEncodeRoot,
LPSTREAM lpstmOut,
HWND hwnd)
{
HRESULT hr;
HRESULT hrCAPI = S_OK;
CCAPIStm capistmMsg(lpstmOut);
IMimePropertySet *pFullProp = NULL, *pBodyProp = NULL;
hr = capistmMsg.HrInitialize(0, hwnd, TRUE, psi, 0, NULL, psi->psldInner);
#ifdef INTEROP2
//N8 This is one half of the fix to do headers correctly
// In the inner, no 822 headers, just MIME
// See another N8 comment for the other half, which is to
// have the outer 822's merged with the inner's MIME headers
// on decode
if (SUCCEEDED(hr)) {
hr = pEncodeRoot->BindToObject(IID_IMimePropertySet, (LPVOID *)&pBodyProp);
}
if (SUCCEEDED(hr)) {
hr = pBodyProp->Clone(&pFullProp);
}
if (SUCCEEDED(hr)) {
LPCSTR rgszHdrKeep[] = {
PIDTOSTR(PID_HDR_CNTTYPE),
PIDTOSTR(PID_HDR_CNTXFER),
PIDTOSTR(PID_HDR_CNTID),
PIDTOSTR(PID_HDR_CNTDESC),
PIDTOSTR(PID_HDR_CNTDISP),
PIDTOSTR(PID_HDR_CNTBASE),
PIDTOSTR(PID_HDR_CNTLOC),
};
hr = pBodyProp->DeleteExcept(ARRAYSIZE(rgszHdrKeep), rgszHdrKeep);
#endif
if (SUCCEEDED(hr)) {
IStream *pstmCapi;
capistmMsg.QueryInterface(IID_IStream, (void**)&pstmCapi);
hr = pTree->SaveBody(hEncodeRoot, 0, pstmCapi);
#if defined(DEBUG) && !defined(MAC)
if (s_fDebugDumpWholeMsg) {
BYTE *pb;
DWORD cb;
if (SUCCEEDED(HrStreamToByte(lpstmOut, &pb, &cb))) {
MemFree(pb);
}
}
#endif
pstmCapi->Release();
hrCAPI = capistmMsg.EndStreaming();
if (SUCCEEDED(hr)) { // hr from the SaveBody takes precedence
hr = hrCAPI;
}
}
#ifdef INTEROP2
}
#endif
if (SUCCEEDED(hr)) {
PROPVARIANT var;
pTree->DeleteBody(hEncodeRoot, DELETE_CHILDREN_ONLY);
pEncodeRoot->EmptyData();
var.ulVal = 0;
SideAssert(SUCCEEDED(pEncodeRoot->SetOption(OID_SECURITY_TYPE, &var)));
SideAssert(SUCCEEDED(pEncodeRoot->SetOption(OID_SECURITY_SIGNATURE_COUNT, &var)));
#ifdef INTEROP2
// reset the propset
pFullProp->CopyProps(0, NULL, pBodyProp);
#endif
//QPTEST DebugDumpStreamToFile(lpstmOut, "c:\\hexin.bin");
hr = pEncodeRoot->SetData(IET_BINARY, STR_CNT_APPLICATION,
STR_SUB_XPKCS7MIME, IID_IStream, (void*)lpstmOut);
if (SUCCEEDED(hr)) {
PROPVARIANT var;
var.vt = VT_UI4;
if (-1 != psi->ietRequested) {
var.ulVal = psi->ietRequested;
}
else {
var.ulVal = IET_BASE64;
}
pEncodeRoot->SetOption(OID_TRANSMIT_BODY_ENCODING, &var);
// Set the properties for the opaque blob as in S/MIGv2 3.3
var.vt = VT_LPSTR;
var.pszVal = (LPSTR)STR_DIS_ATTACHMENT;
pEncodeRoot->SetProp(PIDTOSTR(PID_HDR_CNTDISP), 0, &var);
var.pszVal = (LPSTR)s_szSMIMEP7m;
pEncodeRoot->SetProp(PIDTOSTR(PID_PAR_FILENAME), 0, &var);
pEncodeRoot->SetProp(PIDTOSTR(PID_PAR_NAME), 0, &var);
if (FEncrypt(psi->dwMsgEnhancement))
{
var.pszVal = (LPSTR) STR_SMT_ENVELOPEDDATA;
}
else
{
#ifdef SMIME_V3
if ((psi->pszInnerContent != NULL) &&
(strcmp(psi->pszInnerContent, szOID_SMIME_ContentType_Receipt) == 0))
{
var.pszVal = (LPSTR) STR_SMT_SIGNEDRECEIPT;
}
else
{
var.pszVal = (LPSTR) STR_SMT_SIGNEDDATA;
}
#else // !SMIME_V3
var.pszVal = (LPSTR) STR_SMT_SIGNEDDATA;
#endif // SMIME_V3
}
pEncodeRoot->SetProp(STR_PAR_SMIMETYPE, 0, &var);
}
}
ReleaseObj(pFullProp);
ReleaseObj(pBodyProp);
return hr;
}
///////////////////////////////////////////////////////////////////////////
//
// Decode methods
//
/* DecodeMessage:
**
** Purpose:
** To rip the shroud of secrecy from a message, leaving it naked in the
** harsh light of dawning comprehension. However, this function hides
** the unsightly goings-on that accomplish this task, simply returning
** a radically different tree
** Takes:
** IN pTree - the message's tree
** IN dwFlags - expansion. must be 0
** Returns:
** a variety of good and bad responses
*/
HRESULT CSMime::DecodeMessage(
IMimeMessageTree *const pTree,
DWORD dwFlags)
{
HRESULT hr;
HBODY hRoot;
HWND hwnd = NULL;
IMimeBody * pDecodeRoot = NULL;
PROPVARIANT var;
if (!pTree || (dwFlags & ~SEF_MASK)) {
return TrapError(E_INVALIDARG);
}
if (SUCCEEDED(hr = pTree->GetBody(IBL_ROOT, NULL, &hRoot))) {
CHECKHR(hr = pTree->BindToObject(hRoot, IID_IMimeBody, (void**)&pDecodeRoot));
#ifdef _WIN64
if (SUCCEEDED(pDecodeRoot->GetOption(OID_SECURITY_HWND_OWNER_64, &var)) &&
(NULL != (HWND)(var.pulVal)))
{
Assert(VT_UI8 == var.vt);
hwnd = *(HWND *)(&(var.uhVal));
}
#else
if (SUCCEEDED(pDecodeRoot->GetOption(OID_SECURITY_HWND_OWNER, &var)) &&
(NULL != var.ulVal))
{
Assert(VT_UI4 == var.vt);
hwnd = (HWND)var.ulVal;
}
#endif // _WIN64
hr = TrapError(DecodeBody2(pTree, hRoot, dwFlags|EBF_RECURSE, NULL,
hwnd, NULL));
}
exit:
ReleaseObj(pDecodeRoot);
return hr;
}
HRESULT CSMime::DecodeMessage2(IMimeMessageTree *const pTree, DWORD dwFlags,
HWND hwnd, IMimeSecurityCallback * pCallback)
{
HRESULT hr;
HBODY hRoot;
if (!pTree || (dwFlags & ~SEF_MASK)) {
return TrapError(E_INVALIDARG);
}
if (SUCCEEDED(hr = pTree->GetBody(IBL_ROOT, NULL, &hRoot))) {
hr = TrapError(DecodeBody2(pTree, hRoot, dwFlags|EBF_RECURSE, NULL,
hwnd, pCallback));
}
return hr;
}
/* DecodeBody:
**
** Purpose:
** Do the entirety of the S/MIME decode operation.
**
** Takes:
** IN pTree - the tree of the body to decode
** IN hEncodeRoot - body from which to decode downward
** IN dwFlags - set of DBF_ or SDF_
*/
HRESULT CSMime::DecodeBody(
IMimeMessageTree *const pTree,
HBODY hDecodeRoot,
DWORD dwFlags)
{
return DecodeBody(pTree, hDecodeRoot, dwFlags, NULL);
}
/* DecodeBody:
**
** Purpose:
** The X-rated version of the released (interface) copy. This
** one can tell if it has been recursed and merge the data.
**
** Takes:
** all the scenes of the original film plus:
** IN psiOuterOp - SMIMEINFO from the outer operation
** Note that this doesn't really mean
** subbodys or messages, but nested S/MIME.
** Simplest case is clearsigned in encryption.
*/
HRESULT CSMime::DecodeBody(
IMimeMessageTree *const pTree,
HBODY hDecodeRoot,
DWORD dwFlags,
SMIMEINFO * psiOuterOp)
{
HRESULT hr;
HWND hwnd = NULL;
IMimeBody * pDecodeRoot = NULL;
PROPVARIANT var;
if (! pTree || ! hDecodeRoot) {
hr = TrapError(E_INVALIDARG);
goto exit;
}
// Get the body we are suppose to be encoding
CHECKHR(hr = pTree->BindToObject(hDecodeRoot, IID_IMimeBody, (void**)&pDecodeRoot));
#ifdef _WIN64
if (SUCCEEDED(pDecodeRoot->GetOption(OID_SECURITY_HWND_OWNER_64, &var)) && (NULL != (HWND)(var.pulVal))) {
Assert(VT_UI8 == var.vt);
hwnd = *(HWND *)(&(var.uhVal));
}
#else
if (SUCCEEDED(pDecodeRoot->GetOption(OID_SECURITY_HWND_OWNER, &var)) && (NULL != var.ulVal))
{
Assert(VT_UI4 == var.vt);
hwnd = (HWND)var.ulVal;
}
#endif // _WIN6
hr = DecodeBody2(pTree, hDecodeRoot, dwFlags, psiOuterOp, hwnd, NULL);
exit:
ReleaseObj(pDecodeRoot);
return hr;
}
HRESULT CSMime::DecodeBody2(
IMimeMessageTree *const pTree,
HBODY hDecodeRoot,
DWORD dwFlags,
SMIMEINFO * psiOuterOp,
HWND hwnd,
IMimeSecurityCallback * pCallback)
{
PROPVARIANT var;
IMimeBody * pData;
IMimeBody * pDecodeRoot = NULL;
HRESULT hr, hr_smime = S_OK;
HBODY hSignature, hData;
SMIMEINFO si;
CVirtualStream * pvstmDecoded = NULL;
IStream * pstmMsg = NULL;
if (!(pTree && hDecodeRoot)) {
hr = TrapError(E_INVALIDARG);
goto exit;
}
//
// Get the body we are going to decode
//
CHECKHR(hr = pTree->BindToObject(hDecodeRoot, IID_IMimeBody, (void**)&pDecodeRoot));
memset(&si, 0, sizeof(SMIMEINFO));
OptionsToSMIMEINFO(FALSE, pTree, pDecodeRoot, &si);
//
// If we know what crypt provider to use, then grab it
//
#ifndef _WIN64
if (SUCCEEDED(pDecodeRoot->GetOption(OID_SECURITY_HCRYPTPROV, &var)) &&
(NULL != var.ulVal))
{
Assert(VT_UI4 == var.vt);
si.hProv = var.ulVal;
}
#else // _WIN64
if (SUCCEEDED(pDecodeRoot->GetOption(OID_SECURITY_HCRYPTPROV_64, &var)) &&
(NULL != var.pulVal))
{
Assert(VT_UI8 == var.vt);
si.hProv = (HCRYPTPROV) var.pulVal;
}
#endif //_WIN64
//
// Determine if this is a multi-part signed message, if so then do the appropriate
// decoding. Determined by "content-type: multipart/signed; protocol=pkcs7-signature"
//
if (S_OK == pDecodeRoot->IsContentType(STR_CNT_MULTIPART, STR_SUB_SIGNED) &&
IsSMimeProtocol(pDecodeRoot)) {
CHECKHR(hr = pTree->GetBody(IBL_FIRST, hDecodeRoot, &hData));
CHECKHR(hr = pTree->GetBody(IBL_LAST, hDecodeRoot, &hSignature));
if FAILED(hr_smime = HrDecodeClearSigned(dwFlags, &si, pTree, hData,
hSignature, hwnd, pCallback)) {
goto exit;
}
// now we need to make the message readable. get rid of the signature
// blob. this will leave the m/s with one child, the message data.
// delete the parent with DELETE_PROMOTE_CHILDREN and this will
// kick the data body up as the only remaining body.
pTree->DeleteBody(hSignature, 0);
pTree->DeleteBody(hDecodeRoot, DELETE_PROMOTE_CHILDREN);
pTree->Commit(0);
}
//
// Determine if this is a blob signed or encrypted message. If it is then
// do the appropriate decoding.
//
else if (S_OK == pDecodeRoot->IsContentType(STR_CNT_APPLICATION, STR_SUB_XPKCS7MIME) ||
S_OK == pDecodeRoot->IsContentType(STR_CNT_APPLICATION, STR_SUB_PKCS7MIME)) {
//
// Create the stream to hold the decoded mime body
//
if (!(pvstmDecoded = new CVirtualStream)) {
hr = TrapError(E_OUTOFMEMORY);
goto exit;
}
//
// Do the actual decode of the message. This produces the decode stream in
// pvstmDecode.
//
if (FAILED(hr_smime = HrDecodeOpaque(dwFlags, &si, pDecodeRoot,
pvstmDecoded, hwnd, pCallback))) {
goto exit;
}
// Header bug fix:
// we need to blow away the subtree below this body
// however, we have to keep the 822 headers around. This option
// change does this, mostly. the EmptyData call removes
// some of the body's properties, most notably the content-*
// ones. when I use RELOAD_HEADER_REPLACE, the inner body
// will blow away any of the overlapping outer headers, but
// keep the ones that don't exist.
//
// Why did I do this?
// Deming doesn't put a from: line in the #7 signedData. We
// shoudln't either. Now we don't. Without this fix, this
// would prevent us from being able to add to WAB because
// the address table would be empty.
CHECKHR(hr = pTree->DeleteBody(hDecodeRoot, DELETE_CHILDREN_ONLY));
CHECKHR(hr = pDecodeRoot->EmptyData());
ULONG ulOldval;
pTree->GetOption(OID_HEADER_RELOAD_TYPE, &var);
Assert(VT_UI4 == var.vt);
ulOldval = var.ulVal;
var.ulVal = RELOAD_HEADER_REPLACE;
pTree->SetOption(OID_HEADER_RELOAD_TYPE, &var);
//
// Load the decoded message back into the message so that we can have the
// decrypted message.
//
CHECKHR(hr = pTree->Load(pvstmDecoded));
var.ulVal = ulOldval;
pTree->SetOption(OID_HEADER_RELOAD_TYPE, &var);
//
// Grab the root node -- this is where we are going to put the info relative
// to the decryption/decoding we just did.
//
CHECKHR(hr = pTree->GetBody(IBL_ROOT, NULL, &hData));
}
else {
hr = MIME_S_SECURITY_NOOP;
goto exit;
}
// now, this could be way cool and actually have another S/MIME part inside
// The most common case is a clear signed message inside of encryption, but
// others could occur.
// Recurse!
{
HBODY hRoot;
if (SUCCEEDED(hr = pTree->GetBody(IBL_ROOT, NULL, &hRoot))) {
hr = TrapError(DecodeBody2(pTree, hRoot, dwFlags|EBF_RECURSE, &si,
hwnd, pCallback));
}
CHECKHR(hr);
}
if (hr == MIME_S_SECURITY_NOOP) {
hr = S_OK;
}
else {
hData = HBODY_ROOT;
}
//
// If this is the root of the decode, then we move the decryption information back
// into the message we are dealing with. If this is not the root then merge together
// the S/MIME info structure from the caller.
//
if (psiOuterOp == NULL) {
hr = SMIMEINFOToOptions(pTree, &si, hData);
}
else {
hr = MergeSMIMEINFO(psiOuterOp, &si);
}
Assert(SUCCEEDED(hr));
exit:
ReleaseObj(pDecodeRoot);
ReleaseObj(pstmMsg);
ReleaseObj(pvstmDecoded);
FreeSMIMEINFO(&si);
if (S_OK != hr_smime && SUCCEEDED(hr)) {
hr = (MIME_E_SECURITY_NOOP == hr_smime) ? MIME_S_SECURITY_NOOP : hr_smime;
}
return TrapError(hr);
}
/* HrDecodeOpaque:
**
** Purpose:
**
** Takes:
**
**
*/
HRESULT CSMime::HrDecodeOpaque(DWORD dwFlags,
SMIMEINFO *const psi,
IMimeBody *const pBody,
IStream *const pstmOut,
HWND hwnd,
IMimeSecurityCallback * pCallback)
{
HRESULT hr;
CCAPIStm capistmMsg(pstmOut);
hr = capistmMsg.HrInitialize(dwFlags, hwnd, FALSE, psi, 0, pCallback, NULL);
if (SUCCEEDED(hr)) {
IStream * pstmCapi;
HCRYPTMSG hMsg;
//QPTEST hr = pBody->GetDataHere(IET_BINARY, pstmOut);
//QPTEST DebugDumpStreamToFile(pstmOut, "c:\\stmout.bin");
//QPTEST HrRewindStream(pstmOut);
capistmMsg.QueryInterface(IID_IStream, (void**)&pstmCapi);
hr = pBody->GetDataHere(IET_BINARY, pstmCapi);
pstmCapi->Release();
if (FAILED(hr)) goto exit;
CHECKHR(hr = capistmMsg.EndStreaming());
#if defined(DEBUG) && !defined(MAC)
if (s_fDebugDumpWholeMsg) {
BYTE *pb;
DWORD cb;
if (SUCCEEDED(HrStreamToByte(pstmOut, &pb, &cb))) {
MemFree(pb);
}
}
#endif
hr = CAPISTMtoSMIMEINFO(&capistmMsg, psi);
}
exit:
return hr;
}
/* HrDecodeClearSigned:
**
** Purpose:
** Reform the signed body to be hashed and extract the signature
** data. Pass these through to the interface stream method.
** Takes:
** IN dwFlags - Control Flags for the decode
** IN OUT psi - passed to DecodeDetachedStream
** IN pTree - tree to get message source from
** IN hData - handle to body with signed data
** IN hSig - handle to body with signature
** Returns:
** hresult. no function-specific return values.
*/
HRESULT CSMime::HrDecodeClearSigned(DWORD dwFlags,
SMIMEINFO *const psi,
IMimeMessageTree *const pTree,
const HBODY hData,
const HBODY hSig,
HWND hwnd, IMimeSecurityCallback * pCallback)
{
HRESULT hr, hr_smime=S_OK;
IMimeBody *pData = NULL,
*pSig = NULL;
IStream *pstmSig,
*pstmMsg = NULL;
BODYOFFSETS boData;
LARGE_INTEGER liPos;
ULARGE_INTEGER uliToCopy;
CVirtualStream *pvstmDecoded = NULL;
CCAPIStm capistmMsg(NULL);
#ifdef DEBUG
BLOB blobMsg = {NULL,0};
#endif
CHECKHR(hr = pTree->BindToObject(hData, IID_IMimeBody, (void**)&pData));
CHECKHR(hr = pData->GetOffsets(&boData));
// BUG 38411: I need a clean pristine virginal-white stream
// so we have to go straight to the horse's smelly mouth.
CHECKHR(hr = pTree->GetMessageSource(&pstmMsg, 0));
#if defined(DEBUG) && !defined(MAC)
if (s_fDebugDumpWholeMsg) {
CHECKHR (hr = HrStreamToByte(pstmMsg, &blobMsg.pBlobData, &blobMsg.cbSize));
HrRewindStream(pstmMsg);
}
#endif
// Compute the portion we want of the mondo stream
liPos.HighPart = 0;
liPos.LowPart = boData.cbHeaderStart;
CHECKHR(hr = pstmMsg->Seek(liPos, STREAM_SEEK_SET, NULL));
hr = capistmMsg.HrInitialize(dwFlags, hwnd, FALSE, psi, CSTM_DETACHED, pCallback, NULL);
if (SUCCEEDED(hr)) {
IStream *pstmCapi;
capistmMsg.QueryInterface(IID_IStream, (void**)&pstmCapi);
// Get the signature data and feed it in
hr = pTree->BindToObject(hSig, IID_IMimeBody, (void**)&pSig);
if (SUCCEEDED(hr)) {
hr = pSig->GetDataHere(IET_BINARY, pstmCapi);
if (SUCCEEDED(hr)) {
hr = capistmMsg.EndStreaming();
}
}
uliToCopy.HighPart = 0;
uliToCopy.LowPart = boData.cbBodyEnd-boData.cbHeaderStart;
// Now feed in the actual data to hash
if (SUCCEEDED(hr)) {
hr = pstmMsg->CopyTo(pstmCapi, uliToCopy, NULL, NULL);
}
if (SUCCEEDED(hr)) {
hr = capistmMsg.EndStreaming();
}
if (SUCCEEDED(hr)) {
hr = CAPISTMtoSMIMEINFO(&capistmMsg, psi);
}
pstmCapi->Release();
}
exit:
ReleaseObj(pData);
ReleaseObj(pSig);
ReleaseObj(pstmMsg);
#ifdef DEBUG
if (blobMsg.pBlobData) {
MemFree(blobMsg.pBlobData);
}
#endif
if (S_OK != hr_smime && SUCCEEDED(hr)) {
hr = hr_smime;
}
return hr;
}
HRESULT CSMime::CAPISTMtoSMIMEINFO(CCAPIStm *pcapistm, SMIMEINFO *psi)
{
DWORD i;
PSECURITY_LAYER_DATA psldMessage = pcapistm->GetSecurityLayerData();
psi->dwMsgEnhancement = MST_NONE;
psi->ulMsgValidity = MSV_OK;
#ifdef DEBUG
{
DWORD dwLevel = 0;
PSECURITY_LAYER_DATA psldLoop;
psldLoop = psldMessage;
if (psldLoop) {
SMDOUT("Decode called on:");
}
else {
SMDOUT("Decode called but the message data is NULL.");
}
dwLevel = 0;
while (psldLoop) {
for (DWORD i = 0; i <= dwLevel; i++) {
DebugTrace(" ");
}
if (MST_BLOB_FLAG & psldLoop->m_dwMsgEnhancement) {
SMDOUT("CMSG_SIGNED");
}
else if ((MST_THIS_SIGN | MST_THIS_ENCRYPT) ==
((MST_THIS_SIGN | MST_THIS_ENCRYPT) & psldLoop->m_dwMsgEnhancement)) {
SMDOUT("CMSG_SIGNED_AND_ENVELOPED, uhoh.");
}
else if (MST_THIS_SIGN & psldLoop->m_dwMsgEnhancement) {
SMDOUT("clear signed mail");
}
else if (MST_THIS_ENCRYPT & psldLoop->m_dwMsgEnhancement) {
SMDOUT("CMSG_ENVELOPED");
}
dwLevel++;
psldLoop = psldLoop->m_psldInner;
}
}
#endif
if (psldMessage) {
PSECURITY_LAYER_DATA psldLoop;
Assert(0 == psi->dwMsgEnhancement);
Assert(0 == psi->ulMsgValidity);
Assert(NULL == psi->psldLayers);
Assert(NULL == psi->psldEncrypt);
Assert(NULL == psi->psldInner);
psi->psldLayers = psldMessage;
psi->psldLayers->AddRef();
for (psldLoop = psldMessage; psldLoop != NULL;
psldLoop = psldLoop->m_psldInner) {
psi->dwMsgEnhancement |= psldLoop->m_dwMsgEnhancement;
psi->fCertWithMsg |= psldLoop->m_fCertInLayer;
if (MST_THIS_SIGN & psldLoop->m_dwMsgEnhancement) {
for (i=0; i<psldLoop->m_cSigners; i++) {
psi->ulMsgValidity |= psldLoop->m_rgSigners[i].ulValidity;
}
}
//
// We need to get the inner most encryption item here.
//
if (MST_THIS_ENCRYPT & psldLoop->m_dwMsgEnhancement) {
psi->ulMsgValidity |= psldLoop->m_ulDecValidity;
// Keep track of the innermost encryption layer for easy access
psi->psldEncrypt = psldLoop;
}
if (! psldLoop->m_psldInner) {
psi->psldInner = psldLoop;
}
}
CCAPIStm::FreeSecurityLayerData(psldMessage);
}
// need to handle this stuff again
//hr = MIME_E_SECURITY_CANTDECRYPT;
//case CMSG_SIGNED_AND_ENVELOPED:
//case CMSG_DATA:
//default:
//hr = MIME_E_SECURITY_UNKMSGTYPE;
return S_OK;
}
///////////////////////////////////////////////////////////////////////////
//
// Crytographic transformations... functions that do the real work
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Signature verification and decryption
//
///////////////////////////////////////////////////////////////////////////
//
// Other function sets
//
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Other certificate routines
//
/* GetCertificateName:
**
** Purpose:
**
** Takes:
** IN pX509Cert - certificate from which to snarf name
** IN cn - style of name to return (SIMPLE|OID|X500)
** OUT ppszName - name gets stuffed here. Caller frees.
** Returns:
** hresult
*/
STDMETHODIMP CSMime::GetCertificateName(
const PCX509CERT pX509Cert,
const CERTNAMETYPE cn,
LPSTR * ppszName)
{
DWORD flag, cch;
HRESULT hr;
CHECKSMIMEINIT
if (!(pX509Cert && ppszName)) {
hr = TrapError(E_INVALIDARG);
goto exit;
}
// convert to CAPI flag
switch (cn) {
case SIMPLE:
flag = CERT_SIMPLE_NAME_STR;
break;
case OID:
flag = CERT_OID_NAME_STR;
break;
case X500:
flag = CERT_X500_NAME_STR;
break;
default:
hr = TrapError(E_INVALIDARG);
goto exit;
}
#define pCert ((PCCERT_CONTEXT)pX509Cert)
cch = CertNameToStr(pCert->dwCertEncodingType, &pCert->pCertInfo->Subject,
flag, NULL, 0);
CHECKHR(hr = HrAlloc((void**)ppszName, cch*sizeof(TCHAR)));
CertNameToStr(pCert->dwCertEncodingType, &pCert->pCertInfo->Subject,
flag|CERT_NAME_STR_SEMICOLON_FLAG, *ppszName, cch);
#undef pCert
exit:
return hr;
}
/* HrGetCertsFromThumbprints:
**
** Purpose:
** This version of the function opens the "My" and "AddressBook" stores
** and calls the exposed GetCertsFromThumbprints method.
**
*/
HRESULT CSMime::HrGetCertsFromThumbprints(
THUMBBLOB *const rgThumbprint,
X509CERTRESULT *const pResults)
{
HRESULT hr = E_FAIL;
const DWORD cStores = 2;
HCERTSTORE rgCertStore[cStores];
rgCertStore[0] = OpenCachedMyStore();
if (rgCertStore[0]) {
rgCertStore[1] = OpenCachedAddressBookStore();
if (rgCertStore[1]) {
hr = MimeOleGetCertsFromThumbprints(rgThumbprint, pResults, rgCertStore, cStores);
CertCloseStore(rgCertStore[1], 0);
}
else {
// No WAB store, so there are NO matching certs
CRDOUT("No thumbprints in WAB");
for (ULONG iEntry = 0; iEntry < pResults->cEntries; iEntry++) {
pResults->rgpCert[iEntry] = NULL;
pResults->rgcs[iEntry] = CERTIFICATE_NOPRINT;
}
hr = MIME_S_SECURITY_ERROROCCURED;
}
CertCloseStore(rgCertStore[0], 0);
}
return hr;
}
/* EnumCertificates:
**
** Purpose:
** enumerate certificates from a store
** Takes:
** IN hc - store to query
** IN dwUsage - ITT_* or 0
** maps to a CAPI dwKeyUsage (AT_*)
** IN pPrev - last certificate received from this function
** (NULL to get first cert)
** OUT pCert - certificate next in enumeration
** Notes:
** pPrev and pCert may reference the same variable
** dwUsage may be 0 if caller just wants to check for existance of any certs
** Returns:
** CRYPT_E_NOT_FOUND if no more certs
** S_FALSE if dwUsage is 0 and no certs exist
*/
STDMETHODIMP CSMime::EnumCertificates(
HCAPICERTSTORE hc,
DWORD dwUsage,
PCX509CERT pPrev,
PCX509CERT * ppCert)
{
HRESULT hr;
PCCERT_CONTEXT pNewCert = NULL;
CHECKSMIMEINIT
if (!(hc && (ppCert || !dwUsage))) {
hr = TrapError(E_INVALIDARG);
goto exit;
}
if (ITT_SIGNING == dwUsage || ITT_ENCRYPTION == dwUsage) {
dwUsage = AT_KEYEXCHANGE;
hr = HrFindUsableCert((HCERTSTORE)hc, (BYTE)dwUsage, (PCCERT_CONTEXT)pPrev, &pNewCert);
}
else if (0 == dwUsage) {
pNewCert = CertEnumCertificatesInStore(hc, NULL);
if (pNewCert) {
hr = S_OK;
FreeCert(pNewCert);
}
else {
hr = S_FALSE;
}
}
else {
hr = TrapError(E_INVALIDARG);
}
exit:
if (ppCert) {
*ppCert = (PCX509CERT)pNewCert;
}
return hr;
}
/* HrGetLastError
**
** Purpose:
** Convert a GetLastError value to an HRESULT
** A failure HRESULT must have the high bit set.
**
** Takes:
** none
**
** Returns:
** HRESULT
*/
HRESULT HrGetLastError(void)
{
DWORD error;
HRESULT hr;
error = GetLastError();
if (!(error & 0x80000000)) {
hr = error | 0x80070000; // system error
} else {
hr = (HRESULT)error;
}
return(hr);
}
/* HrFindUsableCert:
**
** Purpose:
** Get a cert out of a cert store that has the right keyspec
** Takes:
** IN hCertStore - store to enumerate
** IN dwKeySpec - AT_SIGNATURE or AT_KEYEXCHANGE
** IN pPrevCert - last certificate received from this function
** (NULL to get first cert)
** OUT ppCert - the matching cert, NULL iff none
** Returns:
** SMIME_E_NOCERT if no cert can be found
*/
HRESULT CSMime::HrFindUsableCert(
HCERTSTORE hCertStore,
BYTE dwKeySpec,
PCCERT_CONTEXT pPrevCert,
PCCERT_CONTEXT *ppCert)
{
PCCERT_CONTEXT pCert;
PCRYPT_KEY_PROV_INFO pKPI;
HRESULT hr = S_OK;
BOOL fFound = FALSE;
PCERT_NAME_INFO pNameInfo;
PCERT_RDN_ATTR pRDNAttr;
#ifdef DEBUG
DWORD count=0;
#endif
LPTSTR lpEmailAddress;
CHECKSMIMEINIT
Assert(hCertStore && ppCert);
Assert(AT_SIGNATURE == dwKeySpec || AT_KEYEXCHANGE == dwKeySpec);
*ppCert = NULL;
pKPI = NULL;
while (!fFound) {
pCert = CertEnumCertificatesInStore(hCertStore, pPrevCert);
if (pCert == NULL) { // no more certs?
break;
}
// Need to find the key provider
pKPI = (PCRYPT_KEY_PROV_INFO)PVGetCertificateParam(pCert, CERT_KEY_PROV_INFO_PROP_ID, NULL);
#ifdef DEBUG
count++;
// We want to be able to send broken mail so that the
// verify/decrypt fails.
if (s_fDebugEmitBroken) {
if (pKPI && pKPI->dwKeySpec != dwKeySpec) fFound = TRUE;
}
else {
if (pKPI && pKPI->dwKeySpec == dwKeySpec) fFound = TRUE;
}
#else
if (pKPI && pKPI->dwKeySpec == dwKeySpec) {
fFound = TRUE;
}
#endif
// Validate the email address
if (fFound && (lpEmailAddress = SzGetCertificateEmailAddress(pCert))) {
MemFree(lpEmailAddress);
}
else {
#ifdef DEBUG
{
SMDOUT("Certificate %d has no email address", count);
if (fFound && !s_fDebugAllowNoEmail) {
fFound = FALSE;
}
}
#else
fFound = FALSE;
#endif
}
if (pKPI) {
MemFree(pKPI);
pKPI = NULL;
}
pPrevCert = pCert; // next enumeration round
} // enum
if (pCert == NULL) {
CRDOUT("Couldn't find a signature cert having a key provider");
hr = HrGetLastError();
if (hr == CRYPT_E_NOT_FOUND) {
hr = S_FALSE;
}
else {
hr = MIME_E_SECURITY_NOCERT;
}
}
#ifdef DEBUG
else {
CRDOUT("Usable cert #%d found with keyspec==%d", count, dwKeySpec);
Assert(pCert);
if (s_fDebugShowFoundCert) {
DisplayCert(pCert);
}
}
#endif
*ppCert = pCert;
return TrapError(hr);
}
///////////////////////////////////////////////////////////////////////////
//
// Other functions
//
///////////////////////////////////////////////////////////////////////////
/* HrGetNeededAddresses:
**
** Purpose:
** get the maximum set of addresses needed for S/MIME to work
** Takes:
** IN dwTypes - class(es) of addresses to get
** IN pTree - source of addresses
** OUT ppEnum - enumerator for found addresses
*/
HRESULT CSMime::HrGetNeededAddresses(
const DWORD dwTypes,
IMimeMessageTree * pTree,
IMimeAddressTable ** ppAdrTable,
IMimeEnumAddressTypes **ppEnum)
{
HRESULT hr;
Assert(ppEnum && ppAdrTable && pTree);
if (SUCCEEDED(hr = pTree->BindToObject(HBODY_ROOT, IID_IMimeAddressTable, (void**)ppAdrTable))) {
hr = (*ppAdrTable)->EnumTypes(dwTypes,
IAP_HANDLE | IAP_ADRTYPE | IAP_SIGNING_PRINT | IAP_ENCRYPTION_PRINT | IAP_CERTSTATE, ppEnum);
}
return hr;
}
/* HrGetThumbprints:
**
** Purpose:
**
** Takes:
** IN pEnum - enumerator walked for source addresses
** IN dwType - which SECURE-type certs are needed
** OUT rgThumbprint - array of found thumbprints
** Returns:
** S_OK, unless a GetProp call fails
** E_FAIL if the loop doesn't run for Count times
*/
HRESULT CSMime::HrGetThumbprints(
IMimeEnumAddressTypes * pEnum,
const ITHUMBPRINTTYPE ittType,
THUMBBLOB *const rgThumbprint)
{
HRESULT hr = S_OK;
ADDRESSPROPS apEntry;
const ULONG numToGet = 1;
ULONG cPrints = 0;
Assert(pEnum && rgThumbprint);
Assert((ITT_SIGNING == ittType) || (ITT_ENCRYPTION == ittType));
Assert(!IsBadWritePtr(rgThumbprint, sizeof(THUMBBLOB)*cPrints));
pEnum->Reset();
while(S_OK == pEnum->Next(numToGet, &apEntry, NULL)) {
// if the print isn't found, we deal with that elsewhere
if (ITT_SIGNING == ittType) {
Assert((apEntry.tbSigning.pBlobData && apEntry.tbSigning.cbSize) ||
(!apEntry.tbSigning.pBlobData && !apEntry.tbSigning.cbSize));
if (apEntry.tbSigning.cbSize) {
// we need to null out the thumbprint flag so print doesn't get
// freed below
rgThumbprint[cPrints].pBlobData = apEntry.tbSigning.pBlobData;
rgThumbprint[cPrints].cbSize = apEntry.tbSigning.cbSize;
apEntry.dwProps &= ~IAP_SIGNING_PRINT;
}
}
else {
Assert(ITT_ENCRYPTION == ittType);
Assert((apEntry.tbEncryption.pBlobData && apEntry.tbEncryption.cbSize) ||
(!apEntry.tbEncryption.pBlobData && !apEntry.tbEncryption.cbSize));
if (apEntry.tbEncryption.cbSize) {
// we need to null out the thumbprint flag so print doesn't get
// freed below
rgThumbprint[cPrints].pBlobData = apEntry.tbEncryption.pBlobData;
rgThumbprint[cPrints].cbSize = apEntry.tbEncryption.cbSize;
apEntry.dwProps &= ~IAP_ENCRYPTION_PRINT;
}
}
cPrints++;
g_pMoleAlloc->FreeAddressProps(&apEntry);
}
return hr;
}
/* HrGetCertificates:
**
** Purpose:
** Get certificates based from thumbprints, then update the address
** table based on the return of that operation
** Takes:
** IN pEnum - enumerator that provides the thumbprints
** IN dwType - which certs are needed
** IN fAlreadyHaveSendersCert - if the sender's cert is already found
** OUT pResults - get an array of certs and the assoc certstates
** pResults->cEntries has the size measured in entries
** Returns:
** SMIME_E_CERTERROR if not all certs were retrieved
*/
HRESULT CSMime::HrGetCertificates(
IMimeAddressTable *const pAdrTable,
IMimeEnumAddressTypes * pEnum,
const DWORD dwType,
const BOOL fAlreadyHaveSendersCert,
CERTARRAY * pcaCerts)
{
HRESULT hr;
THUMBBLOB * rgThumbprint;
ULONG cCerts;
X509CERTRESULT certResults;
Assert(pAdrTable && pEnum && pcaCerts);
pcaCerts->rgpCerts = NULL;
pcaCerts->cCerts = 0;
pEnum->Count(&cCerts);
if (! cCerts) {
return MIME_S_SECURITY_NOOP;
}
if (! MemAlloc((void**)&rgThumbprint, sizeof(THUMBBLOB)*cCerts)) {
return E_OUTOFMEMORY;
}
memset(rgThumbprint, 0, sizeof(THUMBBLOB)*cCerts);
if (! MemAlloc((void**)&certResults.rgpCert, sizeof(PCX509CERT)*cCerts)) {
return E_OUTOFMEMORY;
}
memset(certResults.rgpCert, 0, sizeof(PCX509CERT)*cCerts);
if (! MemAlloc((void**)&certResults.rgcs, sizeof(CERTSTATE)*cCerts)) {
return E_OUTOFMEMORY;
}
memset(certResults.rgcs, 0, sizeof(CERTSTATE)*cCerts);
certResults.cEntries = cCerts;
HrGetThumbprints(pEnum, dwType, rgThumbprint);
hr = HrGetCertsFromThumbprints(rgThumbprint, &certResults);
if (SUCCEEDED(hr)) {
hr = HrGenerateCertsStatus(&certResults, pAdrTable, pEnum, fAlreadyHaveSendersCert);
if (SUCCEEDED(hr)) {
pcaCerts->cCerts = certResults.cEntries;
pcaCerts->rgpCerts = (PCCERT_CONTEXT*)certResults.rgpCert;
}
}
if (FAILED(hr)) {
_FreeCertArray((PCCERT_CONTEXT *)certResults.rgpCert, certResults.cEntries);
}
MemFree(certResults.rgcs);
if (rgThumbprint) {
for (DWORD i = 0; i < cCerts; i++) {
if (rgThumbprint[i].pBlobData) {
MemFree(rgThumbprint[i].pBlobData);
}
}
MemFree(rgThumbprint);
}
return hr;
}
/* HrGenerateCertsStatus:
**
** Purpose:
** No assumptions about the rghr in pResults, this function scans the
** HRESULTs and sets the CERTSTATEs in pAdrTable (1:1 mapping) accordingly
** Takes:
** IN pResults - results to use for CERTSTATEs
** IN pAdrTable - address table to set states on
** IN pEnum - list of address handles
** Returns:
**
*/
HRESULT CSMime::HrGenerateCertsStatus(
X509CERTRESULT * pResults,
IMimeAddressTable *const pAdrTable,
IMimeEnumAddressTypes *const pEnum,
const BOOL fIgnoreSenderError)
{
ADDRESSPROPS apEntry;
ADDRESSPROPS apModify;
UINT i;
const ULONG numToGet = 1;
DWORD dexBogus = (DWORD)-1;
HRESULT hr = S_OK;
// Walk the entire Enumerator
i=0;
pEnum->Reset();
apModify.dwProps = IAP_CERTSTATE;
while(S_OK == pEnum->Next(numToGet, &apEntry, NULL)) {
Assert(apEntry.hAddress);
apModify.certstate = pResults->rgcs[i];
// Why was this added, you ask?
// if the client has set OID_SECURITY_CERT_INCLUDED then
// we need to not return spurious errors about the sender's
// certificate. if we didn't find it but were given it
// elsewhere, say things are cool.
// make sure to run through the whole array because the
// error MIME_S_SECURITY_CERTERROR is the most important
// since the sender cert one is really just a warning.
if (CERTIFICATE_OK != apModify.certstate) {
if (FMissingCert(apModify.certstate)) {
if (apEntry.dwAdrType == IAT_FROM) {
if (fIgnoreSenderError) {
apModify.certstate = CERTIFICATE_OK;
}
else {
// since this can be ignored with fIgnoreSenderError
// it should never override another warning
if (S_OK == hr) {
hr = MIME_S_SECURITY_NOSENDERCERT;
}
}
dexBogus = i;
}
else {
hr = MIME_S_SECURITY_NOCERT;
}
}
else {
hr = MIME_S_SECURITY_CERTERROR;
}
}
SideAssert(SUCCEEDED(pAdrTable->SetProps(apEntry.hAddress, &apModify)));
g_pMoleAlloc->FreeAddressProps(&apEntry);
i++;
}
if (i == pResults->cEntries) { // success
if ((DWORD)-1 != dexBogus) {
// we found the sender and the cert for this entry is NULL.
// CAPI doesn't like null certificates, so replace it
if (dexBogus != pResults->cEntries-1) {
// don't need to dupe b/c the end will no
// longer be included in the count
pResults->rgpCert[dexBogus] = pResults->rgpCert[pResults->cEntries-1];
}
--pResults->cEntries;
}
}
else {
hr = E_FAIL;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////
//
// CAPI wrappers
//
/* GetCertData:
**
** Returns:
** MIME_E_NOT_FOUND if the data is not in the cert
**
** Note:
** Currently only supports CDID_EMAIL
*/
STDMETHODIMP CSMime::GetCertData(
const PCX509CERT pX509Cert,
const CERTDATAID dataid,
LPPROPVARIANT pValue)
{
#define pCert ((PCCERT_CONTEXT)pX509Cert)
PCERT_NAME_INFO pNameInfo = NULL;
PCERT_RDN_ATTR pRDNAttr;
HRESULT hr = MIME_E_NOT_FOUND;
LPSTR szOID;
CHECKSMIMEINIT
if (!(pX509Cert && pCert->pCertInfo) || dataid >= CDID_MAX) {
return TrapError(E_INVALIDARG);
}
switch (dataid) {
case CDID_EMAIL:
// Let msoert handle this request
if (pValue->pszVal = SzGetCertificateEmailAddress(pCert)) {
pValue->vt = VT_LPSTR;
hr = S_OK;
}
break;
// if you add any cases, rewrite the findRDN code below
// I assume IA5 and PhilH's NULL's at the end, leaving me
// a very clean copy
default:
hr = E_INVALIDARG;
goto exit;
}
exit:
return TrapError(hr);
#undef pCert
}
///////////////////////////////////////////////////////////////////////////
//
// Static functions
//
///////////////////////////////////////////////////////////////////////////
/***************************************************************************
Name : _HrConvertHrFromGetCertToEncode
Purpose :
Parameters: hr = input HRESULT
fEncrypt = TRUE if we are encrypting
Returns : HRESULT
Comment :
***************************************************************************/
HRESULT _HrConvertHrFromGetCertToEncode(HRESULT hr, const BOOL fEncrypt)
{
if (MIME_S_SECURITY_NOSENDERCERT == hr)
if (fEncrypt) {
hr = MIME_E_SECURITY_ENCRYPTNOSENDERCERT;
}
else {
hr = MIME_E_SECURITY_NOSIGNINGCERT;
}
else if (MIME_S_SECURITY_CERTERROR == hr) {
hr = MIME_E_SECURITY_CERTERROR;
}
else if (MIME_S_SECURITY_NOCERT == hr) {
hr = MIME_E_SECURITY_NOCERT;
}
return hr;
}
/***************************************************************************
Name : _FreeCertArray
Purpose : Free an array of certs
Parameters: rgpCert = array of cert pointers
cCerts = number of certs in rgpCert
Returns : void
Comment :
***************************************************************************/
void _FreeCertArray(PCCERT_CONTEXT *rgpCert, const UINT cCerts)
{
if (rgpCert) {
for (register DWORD i = 0; i < cCerts; i++) {
ReleaseCert(rgpCert[i]);
}
Assert(ALLOCED(rgpCert));
MemFree(rgpCert);
}
}
#ifdef DEBUG
///////////////////////////////////////////////////////////////////////////
//
// Debugging functions
//
///////////////////////////////////////////////////////////////////////////
/***************************************************************************
Name : DumpAlgorithms
Purpose : Debug dump of algorithms
Parameters: void
Returns : void
Comment :
***************************************************************************/
void CSMime::DumpAlgorithms()
{
DWORD dwAlgCount;
char *ptr = NULL;
DWORD i;
ALG_ID aiAlgid;
DWORD dwBits;
DWORD dwNameLen;
char szName[100];
BYTE pbData[1000];
DWORD dwDataLen;
DWORD dwFlags;
CHAR *pszAlgType = NULL;
HCRYPTPROV hProv;
CryptAcquireContext(&hProv, NULL, NULL, 5, 0);
Assert(hProv);
for(i=0; ;i++) {
if (0==i) {
dwFlags = CRYPT_FIRST;
}
else {
dwFlags = 0;
}
dwDataLen = 1000;
if(!CryptGetProvParam(hProv, PP_ENUMALGS, pbData, &dwDataLen, 0)) {
if (ERROR_NO_MORE_ITEMS == GetLastError()) {
break;
}
else {
Assert(0);
}
}
ptr = (char *)pbData;
aiAlgid = *(ALG_ID *)ptr;
ptr += sizeof(ALG_ID);
dwBits = *(DWORD *)ptr;
ptr += sizeof(DWORD);
dwNameLen = *(DWORD *)ptr;
ptr += sizeof(DWORD);
StrCpyN(szName, ptr, (int)(min(dwNameLen, ARRAYSIZE(szName))));
switch(GET_ALG_CLASS(aiAlgid)) {
case ALG_CLASS_DATA_ENCRYPT:
pszAlgType = "Encrypt ";
break;
case ALG_CLASS_HASH:
pszAlgType = "Hash ";
break;
case ALG_CLASS_KEY_EXCHANGE:
pszAlgType = "Exchange ";
break;
case ALG_CLASS_SIGNATURE:
pszAlgType = "Signature";
break;
default:
pszAlgType = "Unknown ";
}
DOUTL(CRYPT_LEVEL, "Algid:%8.8xh, Bits:%-4d, Type:%s, NameLen:%-2d, Name:%s\n",
aiAlgid, dwBits, pszAlgType, dwNameLen, szName);
}
CryptReleaseContext(hProv, 0);
return;
}
#endif // debug
BOOL FSameAgreeParameters(CMS_RECIPIENT_INFO * pRecipInfo1,
CMS_RECIPIENT_INFO * pRecipInfo2)
{
if (pRecipInfo1->dwU1 != pRecipInfo2->dwU1) return FALSE;
if (pRecipInfo1->dwU1 == CMS_RECIPIENT_INFO_PUBKEY_EPHEMERAL_KEYAGREE) {
if ((lstrcmp(pRecipInfo1->u1.u3.EphemeralAlgorithm.pszObjId,
pRecipInfo2->u1.u3.EphemeralAlgorithm.pszObjId) != 0) ||
(pRecipInfo1->u1.u3.EphemeralAlgorithm.Parameters.cbData !=
pRecipInfo2->u1.u3.EphemeralAlgorithm.Parameters.cbData) ||
(memcmp(pRecipInfo1->u1.u3.EphemeralAlgorithm.Parameters.pbData,
pRecipInfo2->u1.u3.EphemeralAlgorithm.Parameters.pbData,
pRecipInfo1->u1.u3.EphemeralAlgorithm.Parameters.cbData) != 0)){
return FALSE;
}
if ((pRecipInfo1->u1.u3.UserKeyingMaterial.cbData !=
pRecipInfo2->u1.u3.UserKeyingMaterial.cbData) ||
(memcmp(pRecipInfo1->u1.u3.UserKeyingMaterial.pbData,
pRecipInfo2->u1.u3.UserKeyingMaterial.pbData,
pRecipInfo1->u1.u3.UserKeyingMaterial.cbData) != 0)) {
return FALSE;
}
}
else if (pRecipInfo1->dwU1 == CMS_RECIPIENT_INFO_PUBKEY_STATIC_KEYAGREE) {
//
// We don't bother checking the sender cert id. I don't know of
// any reason why the same key would be encoded with different
// identifiers (issuer and serial vs subject key id)
//
if (pRecipInfo1->u1.u4.hprov != pRecipInfo2->u1.u4.hprov) return FALSE;
if (pRecipInfo1->u1.u4.dwKeySpec != pRecipInfo2->u1.u4.dwKeySpec) {
return FALSE;
}
if ((pRecipInfo1->u1.u3.UserKeyingMaterial.cbData !=
pRecipInfo2->u1.u3.UserKeyingMaterial.cbData) ||
(memcmp(pRecipInfo1->u1.u3.UserKeyingMaterial.pbData,
pRecipInfo2->u1.u3.UserKeyingMaterial.pbData,
pRecipInfo1->u1.u3.UserKeyingMaterial.cbData) != 0)) {
return FALSE;
}
}
else return FALSE;
return TRUE;
}
HRESULT ConvertEncryptionLayer(IMimeBody * pBody, IMimeSecurity2 * psm, SMIMEINFO * psi)
{
DWORD cAgree;
DWORD cRecipients;
HRESULT hr;
DWORD i;
DWORD i2;
DWORD iAgree;
DWORD iRecip;
CMSG_KEY_AGREE_RECIPIENT_ENCODE_INFO * pAgree = NULL;
CRYPT_ATTRIBUTE * pattr = NULL;
CMSG_MAIL_LIST_RECIPIENT_ENCODE_INFO * pMailList = NULL;
PSECURITY_LAYER_DATA psld = NULL;
CMSG_KEY_TRANS_RECIPIENT_ENCODE_INFO * pTrans = NULL;
CMS_RECIPIENT_INFO * rgRecipInfo = NULL;
PROPVARIANT var;
PropVariantInit(&var);
//
// Create a layer to hold the encryption information
//
if (! (psld = new CSECURITY_LAYER_DATA)) {
hr = E_OUTOFMEMORY;
goto exit;
}
// Link together the different layers
if (psi->psldLayers == NULL) {
psi->psldLayers = psld;
}
psld->m_psldOuter = psi->psldInner;
if (psld->m_psldOuter != NULL) {
psld->m_psldOuter->m_psldInner = psld;
}
psi->psldInner = psld;
//
// Encryption layers must be blobed -- so or it in.
//
psi->dwMsgEnhancement |= MST_BLOB_FLAG;
psld->m_dwMsgEnhancement = psi->dwMsgEnhancement &
(MST_ENCRYPT_MASK | MST_BLOB_FLAG);
//
// The encryption algorithm may not be encoded the way we need it
// to be. The call will re-encode correctly for CMS.
//
// If we are encrypting, it is an error to not have an encryption
// algorithm.
//
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_ALG_BULK, &var)) &&
(0 != var.blob.cbSize)) {
Assert(VT_BLOB == var.vt);
hr = HrBuildContentEncryptionAlg(psld, &var.blob);
if (hr != S_OK) {
Assert(FALSE);
// goto exit;
}
}
else {
hr = E_INVALIDARG;
goto exit;
}
//
// The call may have provided a specific certificate to be used
// in decrypting the message. This functionality is now obsolete but
// still supported. If no certificate is provided then we will search
// for a valid decryption key.
//
PropVariantClear(&var);
#ifdef _WIN64
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_DECRYPTION_64, &var)) &&
(NULL != (PCCERT_CONTEXT *) var.pulVal))
{
Assert(VT_UI8 == var.vt);
// don't need to dupe the cert, been done in GetOption
psld->m_pccertDecrypt = (PCCERT_CONTEXT) var.pulVal;
#else // !_WIN64
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_DECRYPTION, &var)) &&
(NULL != (PCCERT_CONTEXT *) var.ulVal))
{
Assert(VT_UI4 == var.vt);
// don't need to dupe the cert, been done in GetOption
psld->m_pccertDecrypt = (PCCERT_CONTEXT) var.ulVal;
#endif // _WIN64
// Included a cert in this layer
psld->m_fCertInLayer = TRUE;
}
//
// Allocate the space to hold the Encryption structures we are going
// to pass into the CMS structures.
//
CHECKHR(hr = psm->GetRecipientCount(0, &cRecipients));
if (cRecipients == 0) {
hr = MIME_E_SECURITY_ENCRYPTNOSENDERCERT;
goto exit;
}
if (!MemAlloc((LPVOID *) &psld->m_rgRecipientInfo,
cRecipients * sizeof(CMSG_RECIPIENT_ENCODE_INFO))) {
hr = E_OUTOFMEMORY;
goto exit;
}
memset(psld->m_rgRecipientInfo, 0,
cRecipients*sizeof(CMSG_RECIPIENT_ENCODE_INFO));
//
// Allocate the space to hold the values we currently are holding
//
if (!MemAlloc((LPVOID *) &rgRecipInfo,
cRecipients * sizeof(rgRecipInfo[0]))) {
hr = E_OUTOFMEMORY;
goto exit;
}
memset(rgRecipInfo, 0, cRecipients*sizeof(rgRecipInfo[0]));
CHECKHR(hr = psm->GetRecipient(0, 0, cRecipients, rgRecipInfo));
//
// Walk through and process all of the recipients by copying data
// from the current structure into the CMS version of the structure.
//
// Note that we do no free any of the data allocated in the GetRecipient
// call. The data will be freed as part of freeing the CMS data
// structure instead. Ownership of all data is transfered.
//
for (i=0, iRecip = 0; i<cRecipients; i++) {
switch (rgRecipInfo[i].dwRecipientType) {
//
// If this is set then we must have already processed this item
//
case CMS_RECIPIENT_INFO_TYPE_UNKNOWN:
break;
//
// Key Transport items copy over one-for-one.
//
case CMS_RECIPIENT_INFO_TYPE_KEYTRANS:
// try and allocate the object to hold the data.
if (!MemAlloc((LPVOID *) &pTrans, sizeof (*pTrans))) {
hr = E_OUTOFMEMORY;
goto exit;
}
memset(pTrans, 0, sizeof(*pTrans));
//
// Copy the data over from the old to the new structure.
// Ownership of all data is transfered here.
//
pTrans->cbSize = sizeof(*pTrans);
pTrans->KeyEncryptionAlgorithm = rgRecipInfo[i].KeyEncryptionAlgorithm;
pTrans->pvKeyEncryptionAuxInfo = rgRecipInfo[i].pvKeyEncryptionAuxInfo;
Assert(rgRecipInfo[i].dwU1 == CMS_RECIPIENT_INFO_PUBKEY_KEYTRANS);
pTrans->RecipientPublicKey = rgRecipInfo[i].u1.SubjectPublicKey;
Assert((rgRecipInfo[i].dwU3 == CMS_RECIPIENT_INFO_KEYID_KEY_ID) ||
(rgRecipInfo[i].dwU3 == CMS_RECIPIENT_INFO_KEYID_ISSUERSERIAL));
if (rgRecipInfo[i].dwU3 == CMS_RECIPIENT_INFO_KEYID_KEY_ID) {
pTrans->RecipientId.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
pTrans->RecipientId.KeyId = rgRecipInfo[i].u3.KeyId;
}
else if (rgRecipInfo[i].dwU3 == CMS_RECIPIENT_INFO_KEYID_ISSUERSERIAL) {
pTrans->RecipientId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
pTrans->RecipientId.IssuerSerialNumber = rgRecipInfo[i].u3.IssuerSerial;
}
else {
hr = E_INVALIDARG;
goto exit;
}
//
// The copy has been sucessful so point to the new data.
//
psld->m_rgRecipientInfo[iRecip].dwRecipientChoice = CMSG_KEY_TRANS_RECIPIENT;
psld->m_rgRecipientInfo[iRecip].pKeyTrans = pTrans;
pTrans = NULL;
iRecip += 1;
if(rgRecipInfo[i].pccert)
CertFreeCertificateContext(rgRecipInfo[i].pccert);
break;
//
// Mail List items can also just be copied over from the source
// to the destination structures
//
case CMS_RECIPIENT_INFO_TYPE_MAIL_LIST:
// Allocate the CMS structure to hold this data
if (!MemAlloc((LPVOID *) &pMailList, sizeof(*pMailList))) {
hr = E_OUTOFMEMORY;
goto exit;
}
memset(pMailList, 0, sizeof(*pMailList));
//
// Copy the data over from the old to the new structure.
// Ownership of all data is transfered here.
//
pMailList->cbSize = sizeof(*pMailList);
pMailList->KeyEncryptionAlgorithm = rgRecipInfo[i].KeyEncryptionAlgorithm;
pMailList->pvKeyEncryptionAuxInfo = rgRecipInfo[i].pvKeyEncryptionAuxInfo;
Assert(rgRecipInfo[i].dwU1 == CMS_RECIPIENT_INFO_PUBKEY_PROVIDER);
pMailList->hCryptProv = rgRecipInfo[i].u1.u2.hprov;
pMailList->dwKeyChoice = CMSG_MAIL_LIST_HANDLE_KEY_CHOICE;
pMailList->hKeyEncryptionKey = rgRecipInfo[i].u1.u2.hkey;
Assert(rgRecipInfo[i].dwU3 == CMS_RECIPIENT_INFO_KEYID_KEY_ID);
pMailList->KeyId = rgRecipInfo[i].u3.KeyId;
pMailList->Date = rgRecipInfo[i].filetime;
pMailList->pOtherAttr = rgRecipInfo[i].pOtherAttr;
//
// Copy is successful so point to the data
//
psld->m_rgRecipientInfo[iRecip].dwRecipientChoice = CMSG_MAIL_LIST_RECIPIENT;
psld->m_rgRecipientInfo[iRecip].pMailList = pMailList;
iRecip += 1;
pMailList = NULL;
break;
//
// We need to do some magic with key agree structures.
// Specifically we want to do the following:
// - Combine together all key agrees with the same parameters
// into a single record to pass into the CMS code.
// -
case CMS_RECIPIENT_INFO_TYPE_KEYAGREE:
// allocate the CMS structure to hold this data
if (!MemAlloc((LPVOID *) &pAgree, sizeof(*pAgree))) {
hr = E_OUTOFMEMORY;
goto exit;
}
memset(pAgree, 0, sizeof(*pAgree));
//
// Start by setting up the common parameters used by all of the
// recipients in the common key set.
// Setup the key agreement parameters for encoding and
// so forth.
pAgree->cbSize = sizeof(*pAgree);
// Key Encryption Algorithm -
pAgree->KeyEncryptionAlgorithm = rgRecipInfo[i].KeyEncryptionAlgorithm;
pAgree->pvKeyEncryptionAuxInfo = rgRecipInfo[i].pvKeyEncryptionAuxInfo;
// Key Wrap Algorithm - Derive from the content algorithm
// if not already known.
if (rgRecipInfo[i].KeyWrapAlgorithm.pszObjId != NULL) {
pAgree->KeyWrapAlgorithm = rgRecipInfo[i].KeyWrapAlgorithm;
pAgree->pvKeyWrapAuxInfo = rgRecipInfo[i].pvKeyWrapAuxInfo;
}
else {
hr = HrDeriveKeyWrapAlg(psld, pAgree);
if (FAILED(hr)) {
goto exit;
}
}
// Items which are specificly located accroding to static or
// ephemeral key agreement.
if (rgRecipInfo[i].dwU1 == CMS_RECIPIENT_INFO_PUBKEY_EPHEMERAL_KEYAGREE) {
pAgree->UserKeyingMaterial = rgRecipInfo[i].u1.u3.UserKeyingMaterial;
if (!MemAlloc((LPVOID *) &pAgree->pEphemeralAlgorithm,
sizeof(CRYPT_ALGORITHM_IDENTIFIER))) {
hr = E_OUTOFMEMORY;
goto exit;
}
memset(pAgree->pEphemeralAlgorithm, 0,
sizeof(CRYPT_ALGORITHM_IDENTIFIER));
pAgree->dwKeyChoice = CMSG_KEY_AGREE_EPHEMERAL_KEY_CHOICE;
memcpy(pAgree->pEphemeralAlgorithm,
&rgRecipInfo[i].u1.u3.EphemeralAlgorithm,
sizeof(CRYPT_ALGORITHM_IDENTIFIER));
}
else {
Assert(rgRecipInfo[i].dwU1 == CMS_RECIPIENT_INFO_PUBKEY_STATIC_KEYAGREE);
pAgree->dwKeyChoice = CMSG_KEY_AGREE_STATIC_KEY_CHOICE;
pAgree->UserKeyingMaterial = rgRecipInfo[i].u1.u4.UserKeyingMaterial;
if (!MemAlloc((LPVOID *) &pAgree->pSenderId, sizeof(CERT_ID))) {
hr = E_OUTOFMEMORY;
goto exit;
}
memcpy(pAgree->pSenderId, &rgRecipInfo[i].u1.u4.senderCertId,
sizeof(CERT_ID));
pAgree->hCryptProv = rgRecipInfo[i].u1.u4.hprov;
pAgree->dwKeySpec = rgRecipInfo[i].u1.u4.dwKeySpec;
}
//
// We need to count of common items
//
for (i2=i+1, cAgree = 1; i2<cRecipients; i2++) {
cAgree += FSameAgreeParameters(&rgRecipInfo[i],
&rgRecipInfo[i2]);
}
//
//
//
// Allocate space to hold the set of common key agree recipients
//
if (!MemAlloc((LPVOID *) &pAgree->rgpRecipientEncryptedKeys,
cAgree * sizeof(LPVOID))) {
hr = E_OUTOFMEMORY;
goto exit;
}
pAgree->cRecipientEncryptedKeys = cAgree;
memset(pAgree->rgpRecipientEncryptedKeys, 0, cAgree * sizeof(LPVOID));
for (i2=i, iAgree = 0; i2<cRecipients; i2++) {
if ((i2 != i) && !FSameAgreeParameters(&rgRecipInfo[i],
&rgRecipInfo[i2])) {
continue;
}
if (!MemAlloc((LPVOID *) &pAgree->rgpRecipientEncryptedKeys[iAgree],
sizeof(CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO))) {
hr = E_OUTOFMEMORY;
goto exit;
}
memset(pAgree->rgpRecipientEncryptedKeys[iAgree], 0,
sizeof(CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO));
pAgree->rgpRecipientEncryptedKeys[iAgree]->cbSize = sizeof(CMSG_RECIPIENT_ENCRYPTED_KEY_ENCODE_INFO);
//
// Get the recipients Public Key value
//
if (rgRecipInfo[i2].dwU1 == CMS_RECIPIENT_INFO_PUBKEY_EPHEMERAL_KEYAGREE) {
pAgree->rgpRecipientEncryptedKeys[iAgree]->RecipientPublicKey = rgRecipInfo[i2].u1.u3.SubjectPublicKey;
}
else {
Assert(rgRecipInfo[i2].dwU1 == CMS_RECIPIENT_INFO_PUBKEY_STATIC_KEYAGREE);
pAgree->rgpRecipientEncryptedKeys[iAgree]->RecipientPublicKey = rgRecipInfo[i2].u1.u4.SubjectPublicKey;
}
//
// Get the recipients certificate ID
//
Assert((rgRecipInfo[i2].dwU3 == CMS_RECIPIENT_INFO_KEYID_KEY_ID) ||
(rgRecipInfo[i2].dwU3 == CMS_RECIPIENT_INFO_KEYID_ISSUERSERIAL));
if (rgRecipInfo[i2].dwU3 == CMS_RECIPIENT_INFO_KEYID_KEY_ID) {
pAgree->rgpRecipientEncryptedKeys[iAgree]->RecipientId.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
pAgree->rgpRecipientEncryptedKeys[iAgree]->RecipientId.KeyId = rgRecipInfo[i2].u3.KeyId;
pAgree->rgpRecipientEncryptedKeys[iAgree]->Date = rgRecipInfo[i2].filetime;
pAgree->rgpRecipientEncryptedKeys[iAgree]->pOtherAttr = rgRecipInfo[i2].pOtherAttr;
}
else if (rgRecipInfo[i2].dwU3 == CMS_RECIPIENT_INFO_KEYID_ISSUERSERIAL) {
pAgree->rgpRecipientEncryptedKeys[iAgree]->RecipientId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
pAgree->rgpRecipientEncryptedKeys[iAgree]->RecipientId.IssuerSerialNumber = rgRecipInfo[i2].u3.IssuerSerial;
}
else {
hr = E_INVALIDARG;
goto exit;
}
//
// Zero the data so it does not freed twice. (pointers are now
// in the CMS structure)
//
if (i != i2) {
memset(&rgRecipInfo[i2], 0, sizeof(rgRecipInfo[i2]));
}
iAgree += 1;
}
Assert(iAgree == cAgree);
//
// The copy has been successfully completed. We now move
// the data (and ownership) into new structure.
//
psld->m_rgRecipientInfo[iRecip].dwRecipientChoice = CMSG_KEY_AGREE_RECIPIENT;
psld->m_rgRecipientInfo[iRecip].pKeyAgree = pAgree;
iRecip += 1;
pAgree = NULL;
break;
}
//
// Zero the data so it does not freed twice. (pointers are now
// in the CMS structure)
//
memset(&rgRecipInfo[i], 0, sizeof(rgRecipInfo[i]));
}
psld->m_cEncryptItems = iRecip;
PropVariantClear(&var);
#ifndef _WIN64
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCRYPTPROV, &var)))
{
psi->hProv = (HCRYPTPROV) var.ulVal;
}
#else // _WIN64
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCRYPTPROV_64, &var)))
{
psi->hProv = (HCRYPTPROV) var.pulVal;
}
#endif // _WIN64
//
// Pickup the set of encryption certificate bag
//
PropVariantClear(&var);
#ifdef _WIN64
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_ENCRYPT_CERT_BAG_64, &var))) {
psld->m_hstoreEncrypt = (HCERTSTORE) var.pulVal;
#else
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_ENCRYPT_CERT_BAG, &var))) {
psld->m_hstoreEncrypt = (HCERTSTORE) var.ulVal;
#endif // _WIN64
}
//
// Pick up the unprotected attributes
//
if (g_FSupportV3) {
hr = psm->GetAttribute(0, 0, SMIME_ATTRIBUTE_SET_UNPROTECTED, 0, NULL,
&pattr);
if (FAILED(hr)) {
goto exit;
}
else if (hr == S_OK) {
Assert(pattr != NULL);
CHECKHR(hr = HrCopyCryptDataBlob(&pattr->rgValue[0],
&psld->m_blobUnprotectAttrs));
}
}
hr = S_OK;
exit:
PropVariantClear(&var);
if (rgRecipInfo != NULL) {
for (i=0; i<cRecipients; i++) {
FreeRecipientInfoContent(&rgRecipInfo[i]);
}
SafeMemFree(rgRecipInfo);
}
SafeMemFree(pTrans);
SafeMemFree(pMailList);
if (pAgree != NULL) {
for (i=0; pAgree->cRecipientEncryptedKeys; i++) {
SafeMemFree(pAgree->rgpRecipientEncryptedKeys[i]);
}
SafeMemFree(pAgree->pEphemeralAlgorithm);
SafeMemFree(pAgree->pSenderId);
SafeMemFree(pAgree->rgpRecipientEncryptedKeys);
SafeMemFree(pAgree);
}
SafeMemFree(pattr);
return hr;
}
/***************************************************************************
Name : OptionsToSMIMEINFO
Purpose : Fill the SMIMEINFO from the body properties
Parameters: fEncode = TRUE if encoding
pBody -> Body object
psi -> target SMIMEINFO
Returns : HRESULT
Comment :
***************************************************************************/
HRESULT CSMime::OptionsToSMIMEINFO(
BOOL fEncode,
IMimeMessageTree *const pmm,
IMimeBody * pBody,
SMIMEINFO * psi)
{
CRYPT_ATTRIBUTE attr;
DWORD cb;
DWORD cAgree;
ULONG cCertBag = 0;
DWORD dwSecurity;
FILETIME ftSignTime;
HCERTSTORE hCertStore = NULL;
HRESULT hr = S_OK;
ULONG i;
DWORD i2;
DWORD iAgree;
ULONG iLayer;
DWORD iSigner;
PSECURITY_LAYER_DATA psld = NULL, psldTemp;
BYTE rgb[50];
PCCERT_CONTEXT * rgpccCertSigning = NULL;
PCCERT_CONTEXT * rgpcCertBag = NULL;
PROPVARIANT * rgpvAlgHash = NULL;
PROPVARIANT * rgpvAuthAttr = NULL;
PROPVARIANT * rgpvUnauthAttr = NULL;
IMimeSecurity2 * psm = NULL;
ULONG ulLayers = 0;
CRYPT_ATTR_BLOB valTime;
PROPVARIANT var;
Assert(pBody && psi);
//
// Pick up the security interface on the body. It makes life easier
//
hr = pBody->QueryInterface(IID_IMimeSecurity2, (LPVOID *) &psm);
if (FAILED(hr)) {
goto exit;
}
//
// If we are going to encode the body, then see if the content type is
// "OID/xxx". In this case we encode the body specially and we need to
// get the size of the inner blob.
//
if (fEncode) {
// Must determine the body type to see if it needs special processing
if (pBody->IsContentType("OID", NULL) == S_OK) {
PROPVARIANT var;
var.vt = VT_LPSTR;
if (FAILED(pBody->GetProp(STR_ATT_SUBTYPE, 0, &var))) {
return TrapError(E_FAIL);
}
if (strcmp(var.pszVal, szOID_RSA_data) != 0) {
psi->pszInnerContent = var.pszVal;
if (FAILED(pBody->GetEstimatedSize(IET_BINARY, &psi->cbInnerContent))) {
return TrapError(E_FAIL);
}
}
}
}
if (FAILED(pBody->GetOption(OID_SECURITY_KEY_PROMPT, &var))) {
return TrapError(E_FAIL);
}
Assert(VT_LPWSTR == var.vt);
if (var.pwszVal != NULL) {
psi->pwszKeyPrompt = PszDupW(var.pwszVal);
if (psi->pwszKeyPrompt == NULL) {
return TrapError(E_OUTOFMEMORY);
}
}
//
// Get the security to be applied and put it into the security info layer
// structure
//
if (FAILED(pBody->GetOption(OID_SECURITY_TYPE, &var))) {
return TrapError(E_FAIL);
}
Assert(VT_UI4 == var.vt);
psi->dwMsgEnhancement = var.ulVal;
//
// Start doing the setup for encode operations.
//
if (fEncode) {
//
// We must be doing some type of encoding operation, or we should fail.
//
if (MST_NONE == var.ulVal) {
hr = TrapError(MIME_S_SECURITY_NOOP);
goto exit;
}
else if (!(MST_THIS_MASK & var.ulVal)) {
hr = TrapError(MIME_S_SECURITY_RECURSEONLY);
goto exit;
}
else if (MST_CLASS_SMIME_V1 != (var.ulVal & MST_CLASS_MASK)) {
hr = TrapError(MIME_E_SECURITY_CLASSNOTSUPPORTED);
goto exit;
}
//
// If we are doing multiple layers of encrption, then we require that
// the inner level be blobed and not clear.
//
if ((psi->pszInnerContent != NULL) && !(var.ulVal & MST_BLOB_FLAG)) {
hr = TrapError(E_INVALIDARG);
goto exit;
}
//
// Are we encrypting
//
if (psi->dwMsgEnhancement & MST_ENCRYPT_MASK) {
hr = ConvertEncryptionLayer(pBody, psm, psi);
if (FAILED(hr)) {
goto exit;
}
}
//
// Are we signing?
//
if (psi->dwMsgEnhancement & MST_SIGN_MASK) {
//
// Force in a a signing time
//
GetSystemTimeAsFileTime(&ftSignTime);
cb = sizeof(rgb);
if (CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME,
&ftSignTime, 0, NULL, rgb, &cb)) {
attr.pszObjId = szOID_RSA_signingTime;
attr.cValue = 1;
attr.rgValue = &valTime;
valTime.pbData = rgb;
valTime.cbData = cb;
hr = psm->SetAttribute(SMIME_ATTR_ADD_IF_NOT_EXISTS,
(DWORD) -1, SMIME_ATTRIBUTE_SET_SIGNED,
&attr);
if (FAILED(hr)) {
goto exit;
}
}
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_SIGNATURE_COUNT, &var))) {
Assert(VT_UI4 == var.vt);
ulLayers = var.ulVal;
}
#ifdef _WIN64
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING_RG_64, &var))) {
Assert((VT_VECTOR | VT_UI8) == var.vt);
Assert(var.cauh.cElems == ulLayers);
rgpccCertSigning = (PCCERT_CONTEXT *) (var.cauh.pElems);
}
// These next two are optional
// Furthermore, if we get the first, we don't check the second. BJK - Huh?
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE_64, &var)))
{
Assert(VT_UI8 == var.vt);
hCertStore = (HCERTSTORE) var.pulVal;
#else
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING_RG, &var))) {
Assert((VT_VECTOR | VT_UI4) == var.vt);
Assert(var.caul.cElems == ulLayers);
rgpccCertSigning = (PCCERT_CONTEXT *)var.caul.pElems;
}
// These next two are optional
// Furthermore, if we get the first, we don't check the second. BJK - Huh?
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE, &var)))
{
Assert(VT_UI4 == var.vt);
hCertStore = (HCERTSTORE) var.ulVal;
#endif //_WIN64
}
// NOTE: OID_SECURITY_RG_CERT_BAG is not a per-layer array! It
// is an array of all the certs for the entire message.
#ifndef _WIN64
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_RG_CERT_BAG, &var))) {
Assert((VT_VECTOR | VT_UI4) == var.vt);
cCertBag = var.caul.cElems;
rgpcCertBag = (PCCERT_CONTEXT*)var.caul.pElems;
#else
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_RG_CERT_BAG_64, &var))) {
Assert((VT_VECTOR | VT_UI8) == var.vt);
cCertBag = var.cauh.cElems;
rgpcCertBag = (PCCERT_CONTEXT*)(var.cauh.pElems);
#endif //_WIN64
}
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_ALG_HASH_RG, &var))) {
Assert((VT_VECTOR | VT_VARIANT) == var.vt);
Assert(var.capropvar.cElems == ulLayers);
rgpvAlgHash = var.capropvar.pElems;
}
// Grab the unauthenicated attributes
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_UNAUTHATTR_RG, &var))) {
Assert((VT_VECTOR | VT_VARIANT) == var.vt);
Assert(var.capropvar.cElems == ulLayers);
rgpvUnauthAttr = var.capropvar.pElems;
}
// Grab the initial authenicated attributes
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_AUTHATTR_RG, &var))) {
Assert((VT_VECTOR | VT_VARIANT) == var.vt);
Assert(var.capropvar.cElems == ulLayers);
rgpvAuthAttr = var.capropvar.pElems;
}
// Create a layer to hold the signing information
if (! (psld = new CSECURITY_LAYER_DATA)) {
hr = E_OUTOFMEMORY;
goto exit;
}
// Link together the different layers
if (psi->psldLayers == NULL) {
psi->psldLayers = psld;
}
psld->m_psldOuter = psi->psldInner;
if (psld->m_psldOuter != NULL) {
psld->m_psldOuter->m_psldInner = psld;
}
psi->psldInner = psld;
//
psld->m_dwMsgEnhancement = psi->dwMsgEnhancement &
(MST_SIGN_MASK | MST_BLOB_FLAG);
// Fill in the signing information
if (!MemAlloc((LPVOID *) &psld->m_rgSigners,
sizeof(SignerData)*ulLayers)) {
hr = E_OUTOFMEMORY;
goto exit;
}
memset(psld->m_rgSigners, 0, sizeof(SignerData)*ulLayers);
psld->m_cSigners = ulLayers;
for (iSigner=0; iSigner < ulLayers; iSigner++) {
psld->m_rgSigners[iSigner].pccert = DupCert(rgpccCertSigning[iSigner]);
HrCopyBlob(&rgpvAlgHash[iSigner].blob,
&psld->m_rgSigners[iSigner].blobHashAlg);
HrCopyBlob(&rgpvUnauthAttr[iSigner].blob,
&psld->m_rgSigners[iSigner].blobUnauth);
HrCopyBlob(&rgpvAuthAttr[iSigner].blob,
&psld->m_rgSigners[iSigner].blobAuth);
}
#ifdef _WIN64
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE_64, &var)))
{
// Don't addref as we are just giving it to the sld object
psld->m_hcertstor = (HCERTSTORE) var.pulVal;
#else
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE, &var)))
{
// Don't addref as we are just giving it to the sld object
psld->m_hcertstor = (HCERTSTORE) var.ulVal;
#endif // _WIN64
}
}
//
// read these for both signing and encryption
//
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_REQUESTED_CTE, &var))) {
Assert(VT_I4 == var.vt);
psi->ietRequested = ENCODINGTYPE(var.lVal);
}
else {
psi->ietRequested = ENCODINGTYPE(-1);
}
}
//
// read these for both encoding and decoding
//
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_SEARCHSTORES, &var)) &&
#ifdef _WIN64
(NULL != var.cauh.pElems)
#else
(NULL != var.caul.pElems)
#endif // _WIN64
) {
#ifdef _WIN64
CAUH& caul = var.cauh; // On Win64, it's really a cauh
#else
CAUL& caul = var.caul;
#endif // _WIN64
psi->rgStores = (HCERTSTORE*)g_pMoleAlloc->Alloc(caul.cElems * sizeof(HCERTSTORE));
if (psi->rgStores) {
for (DWORD i = 0; i < caul.cElems; i++) {
// Shouldn't have to duplicate since we're not freeing them here
#ifdef _WIN64
psi->rgStores[i] = (HCERTSTORE)caul.pElems[i].QuadPart;
#else
psi->rgStores[i] = (HCERTSTORE)caul.pElems[i];
#endif // _WIN64
}
psi->cStores = caul.cElems;
}
SafeMemFree(caul.pElems);
}
else {
// if we were given no stores, open "My"
psi->rgStores = (HCERTSTORE*)g_pMoleAlloc->Alloc(sizeof(HCERTSTORE));
if (psi->rgStores) {
psi->rgStores[0] = OpenCachedMyStore();
if (psi->rgStores[0]) {
psi->cStores = 1;
} else {
g_pMoleAlloc->Free(psi->rgStores);
}
}
}
exit:
#ifdef SMIME_V3
if (psm != NULL) psm->Release();
#endif // SMIME_V3
#ifndef SMIME_V3
SafeMemFree(rgftSigningTime);
#endif // SMIME_V3
if (rgpccCertSigning) {
for (iLayer = 0; iLayer < ulLayers; iLayer++) {
if (rgpccCertSigning[iLayer]) {
CertFreeCertificateContext(rgpccCertSigning[iLayer]);
}
}
}
SafeMemFree(rgpccCertSigning);
if (hCertStore) {
CertCloseStore(hCertStore, 0);
}
if (rgpcCertBag) {
// NOTE: rgpcCertBag isn't indexed by layer!
for (i = 0; i < cCertBag; i++) {
if (rgpcCertBag[i]) {
CertFreeCertificateContext(rgpcCertBag[i]);
}
}
}
SafeMemFree(rgpcCertBag);
if (rgpvAlgHash) {
for (iLayer = 0; iLayer < ulLayers; iLayer++) {
SafeMemFree(rgpvAlgHash[iLayer].blob.pBlobData);
}
}
SafeMemFree(rgpvAlgHash);
if (rgpvUnauthAttr) {
for (iLayer = 0; iLayer < ulLayers; iLayer++) {
SafeMemFree(rgpvUnauthAttr[iLayer].blob.pBlobData);
}
}
SafeMemFree(rgpvUnauthAttr);
if (rgpvAuthAttr) {
for (iLayer = 0; iLayer < ulLayers; iLayer++) {
SafeMemFree(rgpvAuthAttr[iLayer].blob.pBlobData);
}
}
SafeMemFree(rgpvAuthAttr);
#ifndef SMIME_V3
if (rgpvSymcaps) {
for (iLayer = 0; iLayer < ulLayers; iLayer++) {
SafeMemFree(rgpvSymcaps[iLayer].blob.pBlobData);
}
}
SafeMemFree(rgpvSymcaps);
#endif // !SMIME_V3
return(hr);
}
/***************************************************************************
Name : SMIMEINFOToOptions
Purpose : set the body properties based on contents of the SMIMEINFO
Parameters: psi -> source SMIMEINFO
pBody -> target body object
Returns : void
Comment :
***************************************************************************/
HRESULT CSMime::SMIMEINFOToOptions(
IMimeMessageTree * const pTree,
const SMIMEINFO * psi,
HBODY hBody)
{
DWORD dwSecurityType = 0;
HBODY hBody2;
HRESULT hr;
ULONG iLayer;
PCRYPT_ATTRIBUTES pattrsUnprot = NULL;
CMessageBody * pPrivBody = NULL;
PSECURITY_LAYER_DATA psldLoop;
IMimeSecurity2 * psm = NULL;
PROPVARIANT var;
#ifdef _WIN64
UNALIGNED CRYPT_ATTR_BLOB *pVal = NULL;
#endif
Assert(psi && hBody);
CHECKHR(hr = pTree->BindToObject(hBody, IID_CMessageBody, (void**)&pPrivBody));
Assert(pPrivBody);
pPrivBody->GetOption(OID_SECURITY_CERT_INCLUDED, &var);
Assert(VT_BOOL == var.vt);
if (!var.boolVal && psi->fCertWithMsg) {
var.boolVal = (VARIANT_BOOL) !!psi->fCertWithMsg;
pPrivBody->InternalSetOption(OID_SECURITY_CERT_INCLUDED, &var,
TRUE, TRUE);
}
pPrivBody->GetOption(OID_SECURITY_TYPE, &var);
dwSecurityType = var.ulVal;
// We could have been passed something like ROOT, move to a real handle
pPrivBody->GetHandle(&hBody2);
for (iLayer = 0, psldLoop = psi->psldLayers; psldLoop != NULL;
psldLoop = psldLoop->m_psldInner, iLayer++) {
// The rule here is: Always insert a new layer UNLESS:
// 1. this is an encrpytion layer and
// 2. the previous layer was a signing layer
if ((dwSecurityType != 0) &&
(!(dwSecurityType & MST_THIS_ENCRYPT) ||
!(psldLoop->m_dwMsgEnhancement & MST_THIS_SIGN))) {
/* ((dwSecurityType & MST_THIS_SIGN_ENCRYPT) != MST_THIS_SIGN) &&
((psldLoop->m_dwMsgEnhancement & MST_THIS_SIGN_ENCRYPT) != MST_THIS_ENCRYPT)) {
*/
if (psm != NULL) {
psm->Release();
psm = NULL;
}
pPrivBody->Release();
CHECKHR(hr = pTree->ToMultipart(hBody2, "y-security", NULL));
CHECKHR(hr = pTree->BindToObject(hBody2, IID_CMessageBody,
(void**)&pPrivBody));
}
// This must be after the above check for inserting a layer
dwSecurityType |= psldLoop->m_dwMsgEnhancement | MST_CLASS_SMIME_V1;
if (MST_SIGN_MASK & psldLoop->m_dwMsgEnhancement) {
DWORD cSigners = psldLoop->m_cSigners;
DWORD iSigner;
SignerData * pSigner;
// Allocate enough space in the option arrays for this layer
// OID_SECURITY_TYPE_RG // DWORD
// OID_SECURITY_ALG_HASH_RG // BLOB
// OID_SECURITY_CERT_SIGNING_RG // DWORD
// OID_SECURITY_HCERTSTORE_RG // DWORD
// OID_SECURITY_SYMCAPS_RG // BLOB
// OID_SECURITY_AUTHATTR_RG // BLOB
// OID_SECURITY_UNAUTHATTR_RG // BLOB
// OID_SECURITY_SIGNTIME_RG // FILETIME
// OID_SECURITY_USER_VALIDITY_RG // DWORD
// OID_SECURITY_RO_MSG_VALIDITY_RG // DWORD
#ifdef _WIN64
ULONG cbrgullCertSigning = cSigners * sizeof(ULONGLONG);
ULONGLONG * rgullCertSigning;
#else // !_WIN64
ULONG cbrgdwCertSigning = cSigners * sizeof(DWORD);
DWORD * rgdwCertSigning;
#endif // _WIN64
ULONG cbrgdwUserValidity = cSigners * sizeof(DWORD);
ULONG cbrgdwROMsgValidity = cSigners * sizeof(DWORD);
ULONG cbrgftSigntime = cSigners * sizeof(FILETIME);
ULONG cbrgpvAlgHash = cSigners * sizeof(PROPVARIANT);
ULONG cbrgpvSymcaps = cSigners * sizeof(PROPVARIANT);
ULONG cbrgpvAuthattr = cSigners * sizeof(PROPVARIANT);
ULONG cbrgpvUnauthattr = cSigners * sizeof(PROPVARIANT);
#ifdef SMIME_V3
ULONG cbrgpvReceipt = cSigners * sizeof(PROPVARIANT);
ULONG cbrgpvHash = cSigners * sizeof(PROPVARIANT);
#endif // SMIME_V3
#ifdef _WIN64
ULONG cbArrays = cbrgullCertSigning +
#else
ULONG cbArrays = cbrgdwCertSigning +
#endif
cbrgdwUserValidity + cbrgdwROMsgValidity +
cbrgftSigntime + cbrgpvAlgHash + cbrgpvSymcaps + cbrgpvAuthattr +
#ifdef SMIME_V3
cbrgpvReceipt + cbrgpvHash +
#endif // SMIME_V3
cbrgpvUnauthattr;
LPBYTE lpbArrays = NULL;
LPDWORD rgdwUserValidity;
LPDWORD rgdwROMsgValidity;
FILETIME * rgftSigntime;
PROPVARIANT * rgpvAlgHash;
PROPVARIANT * rgpvSymcaps;
PROPVARIANT * rgpvAuthattr;
PROPVARIANT * rgpvUnauthattr;
#ifdef SMIME_V3
PROPVARIANT * rgpvReceipt;
PROPVARIANT * rgpvHash;
#endif // SMIME_V3
// Allocate them all at once.
if (! MemAlloc((LPVOID *)&lpbArrays, cbArrays)) {
return E_OUTOFMEMORY;
}
ZeroMemory(lpbArrays, cbArrays);
// Set up the pointers
#ifdef _WIN64
rgullCertSigning = (ULONGLONG *) lpbArrays;
rgdwUserValidity = (LPDWORD)((LPBYTE)rgullCertSigning + cbrgullCertSigning);
#else // !_WIN64
rgdwCertSigning = (DWORD *) lpbArrays;
rgdwUserValidity = (LPDWORD)((LPBYTE)rgdwCertSigning + cbrgdwCertSigning);
#endif // _WIN64
rgdwROMsgValidity = (LPDWORD)((LPBYTE)rgdwUserValidity + cbrgdwUserValidity);
rgftSigntime = (FILETIME *)((LPBYTE)rgdwROMsgValidity + cbrgdwROMsgValidity);
rgpvAlgHash = (PROPVARIANT *)((LPBYTE)rgftSigntime + cbrgftSigntime);
rgpvSymcaps = (PROPVARIANT *)((LPBYTE)rgpvAlgHash + cbrgpvAlgHash);
rgpvAuthattr = (PROPVARIANT *)((LPBYTE)rgpvSymcaps + cbrgpvSymcaps);
rgpvUnauthattr = (PROPVARIANT *)((LPBYTE)rgpvAuthattr + cbrgpvAuthattr);
#ifdef SMIME_V3
rgpvReceipt = (PROPVARIANT *)((LPBYTE)rgpvUnauthattr + cbrgpvUnauthattr);
rgpvHash = (PROPVARIANT *)((LPBYTE)rgpvReceipt + cbrgpvReceipt);
#endif // SMIME_V3
for (iSigner=0, pSigner = psldLoop->m_rgSigners; iSigner<cSigners;
iSigner++, pSigner++) {
#ifdef _WIN64
rgullCertSigning[iSigner] = (ULONGLONG) pSigner->pccert;
#else // !_WIN64
rgdwCertSigning[iSigner] = (DWORD) pSigner->pccert;
#endif // _WIN64
if (pSigner->blobHashAlg.cbSize) {
rgpvAlgHash[iSigner].vt = VT_BLOB;
rgpvAlgHash[iSigner].blob.cbSize = pSigner->blobHashAlg.cbSize;
// Don't need to duplicate
rgpvAlgHash[iSigner].blob.pBlobData = pSigner->blobHashAlg.pBlobData;
}
if (pSigner->blobAuth.cbSize) {
rgpvAuthattr[iSigner].vt = VT_BLOB;
rgpvAuthattr[iSigner].blob.cbSize = pSigner->blobAuth.cbSize;
// Don't need to duplicate
rgpvAuthattr[iSigner].blob.pBlobData = pSigner->blobAuth.pBlobData;
// We want to break out two values from the authenticated blobs and put
// them into someplace easy to access
DWORD cbData = 0;
BOOL f;
DWORD i;
PCRYPT_ATTRIBUTES pattrs = NULL;
if ((! HrDecodeObject(pSigner->blobAuth.pBlobData,
pSigner->blobAuth.cbSize,
szOID_Microsoft_Attribute_Sequence, 0, &cbData,
(LPVOID *)&pattrs)) && pattrs) {
FILETIME * pSigningTime = NULL;
Assert(pattrs);
for (i = 0; i < pattrs->cAttr; i++) {
Assert(pattrs->rgAttr[i].cValue == 1);
if (lstrcmp(pattrs->rgAttr[i].pszObjId, szOID_RSA_signingTime) == 0) {
#ifndef _WIN64
if ((! HrDecodeObject(pattrs->rgAttr[i].rgValue[0].pbData,
pattrs->rgAttr[i].rgValue[0].cbData,
X509_CHOICE_OF_TIME, 0, &cbData, (LPVOID *)&pSigningTime)) && pSigningTime)
#else // _WIN64
pVal = &(pattrs->rgAttr[i].rgValue[0]);
if ((! HrDecodeObject(pVal->pbData,
pVal->cbData,
X509_CHOICE_OF_TIME, 0, &cbData, (LPVOID *)&pSigningTime)) && pSigningTime)
#endif //_WIN64
{
Assert(cbData == sizeof(FILETIME));
memcpy(&rgftSigntime[iSigner], pSigningTime, sizeof(FILETIME));
SafeMemFree(pSigningTime);
}
}
else if (lstrcmp(pattrs->rgAttr[i].pszObjId, szOID_RSA_SMIMECapabilities) == 0) {
rgpvSymcaps[iSigner].vt = VT_BLOB;
#ifndef _WIN64
rgpvSymcaps[iSigner].blob.cbSize = pattrs->rgAttr[i].rgValue[0].cbData;
// Duplicate the blob data since the MemFree(pattrs) will nuke this pointer.
rgpvSymcaps[iSigner].blob.pBlobData =
DuplicateMemory(pattrs->rgAttr[i].rgValue[0].pbData, rgpvSymcaps[iSigner].blob.cbSize);
#else // _WIN64
pVal = &(pattrs->rgAttr[i].rgValue[0]);
rgpvSymcaps[iSigner].blob.cbSize = pVal->cbData;
// Duplicate the blob data since the MemFree(pattrs) will nuke this pointer.
rgpvSymcaps[iSigner].blob.pBlobData =
DuplicateMemory(pVal->pbData, rgpvSymcaps[iSigner].blob.cbSize);
#endif //_WIN64
}
}
MemFree(pattrs);
}
}
if (pSigner->blobUnauth.cbSize) {
rgpvUnauthattr[iSigner].vt = VT_BLOB;
rgpvUnauthattr[iSigner].blob.cbSize = pSigner->blobUnauth.cbSize;
// Don't need to duplicate
rgpvUnauthattr[iSigner].blob.pBlobData = pSigner->blobUnauth.pBlobData;
}
#ifdef SMIME_V3
if (pSigner->blobReceipt.cbSize) {
rgpvReceipt[iSigner].vt = VT_BLOB;
rgpvReceipt[iSigner].blob.cbSize = pSigner->blobReceipt.cbSize;
// Don't need to duplicate
rgpvReceipt[iSigner].blob.pBlobData = pSigner->blobReceipt.pBlobData;
dwSecurityType |= MST_RECEIPT_REQUEST;
}
if (pSigner->blobHash.cbSize) {
rgpvHash[iSigner].vt = VT_BLOB;
rgpvHash[iSigner].blob.cbSize = pSigner->blobHash.cbSize;
// Don't need to duplicate
rgpvHash[iSigner].blob.pBlobData = pSigner->blobHash.pBlobData;
}
#endif // SMIME_V3
// Signature validity
rgdwROMsgValidity[iSigner] = pSigner->ulValidity;
}
#ifdef SMIME_V3
var.vt = VT_VECTOR | VT_VARIANT;
var.capropvar.pElems = rgpvReceipt;
var.capropvar.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_RECEIPT_RG, &var, TRUE, TRUE);
var.vt = VT_VECTOR | VT_VARIANT;
var.capropvar.pElems = rgpvHash;
var.capropvar.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_MESSAGE_HASH_RG, &var, TRUE, TRUE);
#endif // SMIME_V3
// Set the array options
#ifdef _WIN64
var.vt = VT_VECTOR | VT_UI8;
var.cauh.pElems = (ULARGE_INTEGER *)(rgullCertSigning);
var.cauh.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_CERT_SIGNING_RG_64, &var, TRUE, TRUE);
#else
var.vt = VT_VECTOR | VT_UI4;
var.caul.pElems = rgdwCertSigning;
var.caul.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_CERT_SIGNING_RG, &var, TRUE, TRUE);
#endif // _WIN64
var.vt = VT_VECTOR | VT_UI4;
var.caul.pElems = rgdwUserValidity;
var.caul.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_USER_VALIDITY_RG, &var, TRUE, TRUE);
var.vt = VT_VECTOR | VT_UI4;
var.caul.pElems = rgdwROMsgValidity;
var.caul.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_RO_MSG_VALIDITY_RG, &var, TRUE, TRUE);
var.vt = VT_VECTOR | VT_FILETIME;
var.cafiletime.pElems = rgftSigntime;
var.cafiletime.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_SIGNTIME_RG, &var, TRUE, TRUE);
var.vt = VT_VECTOR | VT_VARIANT;
var.capropvar.pElems = rgpvAlgHash;
var.capropvar.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_ALG_HASH_RG, &var, TRUE, TRUE);
var.vt = VT_VECTOR | VT_VARIANT;
var.capropvar.pElems = rgpvSymcaps;
var.capropvar.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_SYMCAPS_RG, &var, TRUE, TRUE);
var.vt = VT_VECTOR | VT_VARIANT;
var.capropvar.pElems = rgpvAuthattr;
var.capropvar.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_AUTHATTR_RG, &var, TRUE, TRUE);
var.vt = VT_VECTOR | VT_VARIANT;
var.capropvar.pElems = rgpvUnauthattr;
var.capropvar.cElems = cSigners;
pPrivBody->InternalSetOption(OID_SECURITY_UNAUTHATTR_RG, &var, TRUE, TRUE);
// clean up the duplicated rgpvSymcaps
if (rgpvSymcaps) {
for (iSigner = 0; iSigner < cSigners; iSigner++) {
SafeMemFree(rgpvSymcaps[iSigner].blob.pBlobData);
}
}
// Free the arrays
SafeMemFree(lpbArrays);
}
if (MST_THIS_ENCRYPT & psldLoop->m_dwMsgEnhancement) {
Assert(psldLoop->m_pccertDecrypt != NULL);
if (psldLoop->m_pccertDecrypt != NULL) {
#ifdef _WIN64
var.vt = VT_UI8;
var.pulVal = (ULONG *) (psldLoop->m_pccertDecrypt);
pPrivBody->InternalSetOption(OID_SECURITY_CERT_DECRYPTION_64, &var,
TRUE, TRUE);
#else // !_WIN64
var.vt = VT_UI4;
var.ulVal = (ULONG) psldLoop->m_pccertDecrypt;
pPrivBody->InternalSetOption(OID_SECURITY_CERT_DECRYPTION, &var,
TRUE, TRUE);
#endif // _WIN64
}
//N TODO: convert oids to symcaps
//BruceK: Why? Does this make sense on an encrypted layer? Maybe Erik is
// suggesting that we put can gather a little bit of information about
// the sender's capabilities from the encryption method he used. At any
// rate, it isn't nearly as important as the signed message case.
Assert(psldLoop->m_blobDecAlg.cbSize != 0);
if (psldLoop->m_blobDecAlg.cbSize) {
var.vt = VT_BLOB;
var.blob.cbSize = psldLoop->m_blobDecAlg.cbSize;
var.blob.pBlobData = psldLoop->m_blobDecAlg.pBlobData;
pPrivBody->InternalSetOption(OID_SECURITY_ALG_BULK, &var,
TRUE, TRUE);
}
if (g_FSupportV3 && (psldLoop->m_blobUnprotectAttrs.cbData > 0)) {
DWORD iAttr;
DWORD cbData = 0;
if (psm == NULL) {
CHECKHR(hr = pPrivBody->QueryInterface(IID_IMimeSecurity2, (LPVOID *) &psm));
}
CHECKHR(hr = HrDecodeObject(psldLoop->m_blobUnprotectAttrs.pbData,
psldLoop->m_blobUnprotectAttrs.cbData,
szOID_Microsoft_Attribute_Sequence, 0,
&cbData, (LPVOID *) &pattrsUnprot));
for (iAttr = 0; iAttr < pattrsUnprot->cAttr; iAttr++) {
CHECKHR(hr = psm->SetAttribute(0, 0, SMIME_ATTRIBUTE_SET_UNPROTECTED,
&pattrsUnprot->rgAttr[iAttr]));
}
}
}
var.vt = VT_UI4;
var.ulVal = dwSecurityType;
pPrivBody->InternalSetOption(OID_SECURITY_TYPE, &var, TRUE, TRUE);
// M00BUG -- must deal with two?
#ifdef _WIN64
if (psldLoop->m_hcertstor != NULL) {
var.vt = VT_UI8;
var.pulVal = (ULONG *) (psldLoop->m_hcertstor);
pPrivBody->InternalSetOption(OID_SECURITY_HCERTSTORE_64, &var, TRUE, TRUE);
}
#else
if (psldLoop->m_hcertstor != NULL)
{
var.vt = VT_UI4;
var.ulVal = (ULONG)psldLoop->m_hcertstor;
pPrivBody->InternalSetOption(OID_SECURITY_HCERTSTORE, &var, TRUE, TRUE);
}
#endif // _WIN64
}
hr = S_OK;
exit:
if (psm != NULL) psm->Release();
SafeMemFree(pattrsUnprot);
if (pPrivBody != NULL) pPrivBody->Release();
return hr;
}
/***************************************************************************
Name : MergeSMIMEINFO
Purpose :
Parameters: psiOuter -> Object to be merged into
psiInner -> Object to merge from
Returns : HRESULT of errors
Comment :
***************************************************************************/
HRESULT CSMime::MergeSMIMEINFO(SMIMEINFO *psiOuter, SMIMEINFO * psiInner)
{
PSECURITY_LAYER_DATA psld;
psiOuter->fCertWithMsg |= psiInner->fCertWithMsg;
// Just stick the inner data at the inner most point in the outer data
// and the clear out the inner data structure.
if (psiInner->psldLayers != NULL) {
for (psld = psiOuter->psldLayers; psld->m_psldInner != NULL;
psld = psld->m_psldInner);
psld->m_psldInner = psiInner->psldLayers;
psiInner->psldLayers = NULL;
}
return S_OK;
}
/***************************************************************************
Name : FreeSMIMEINFO
Purpose : Free and Release the memory and objects stored in the
SMIMEINFO.
Parameters: psi -> SMIMEINFO
Returns : none
Comment :
***************************************************************************/
void CSMime::FreeSMIMEINFO(SMIMEINFO *psi)
{
register DWORD i;
if (psi->psldLayers) {
psi->psldLayers->Release();
}
for (i = 0; i < psi->cStores; i++) {
CertCloseStore(psi->rgStores[i], 0);
}
if (psi->rgStores) {
g_pMoleAlloc->Free(psi->rgStores);
}
if (psi->hProv) {
CryptReleaseContext(psi->hProv, 0);
}
#ifdef SMIME_V3
MemFree(psi->pszInnerContent);
SafeMemFree(psi->pwszKeyPrompt);
#endif // SMIME_V3
return;
}
#ifdef KILL_THIS
/***************************************************************************
Name : MergeSMIMEINFOs
Purpose : Merge two SMIMEINFO structures into one
Parameters: psiOuter -> Source structure
psiInner -> Destination structure
Returns : void
Comment :
***************************************************************************/
void CSMime::MergeSMIMEINFOs(const SMIMEINFO *const psiOuter, SMIMEINFO *const psiInner)
{
PSECURITY_LAYER_DATA psldLoopOuter;
PSECURITY_LAYER_DATA psldLoopInner;
psiInner->fCertWithMsg |= psiOuter->fCertWithMsg;
Assert(0 == (psiOuter->ulMsgValidity & psiInner->ulMsgValidity));
psiInner->ulMsgValidity |= psiOuter->ulMsgValidity;
if (psiOuter->dwMsgEnhancement & MST_SIGN_MASK) {
// Duplicate the stores
if (psiOuter->cStores) {
psiInner->rgStores = (HCERTSTORE*)
g_pMoleAlloc->Alloc(psiOuter->cStores * sizeof(HCERTSTORE));
if (psiOuter->rgStores) {
for (DWORD i = 0; i < psiOuter->cStores; i++) {
psiInner->rgStores[i] = CertDuplicateStore(psiOuter->rgStores[i]);
}
psiInner->cStores = psiOuter->cStores;
}
}
}
psiInner->dwMsgEnhancement |= psiOuter->dwMsgEnhancement;
// Before I link in this new list, I'd like to do some error checking. In particular, I
// want to aviod loops and duplicates in my linked list.
psldLoopOuter = psiOuter->psldLayers;
while (psldLoopOuter) {
psldLoopInner = psiInner->psldLayers;
while (psldLoopInner) {
if (psldLoopInner == psldLoopOuter) {
AssertSz(FALSE, "MergeSMIMEINFOs found duplicate layer data");
// OK, we'll just ignore the outer layer data.
goto exit;
}
psldLoopInner = psldLoopInner->m_psldInner;
}
psldLoopOuter = psldLoopOuter->m_psldInner;
}
// Insert the outer layer data list at the head of the inner list.
psldLoopInner = psiInner->psldLayers;
psldLoopOuter = psiOuter->psldLayers;
psiInner->psldLayers = psldLoopOuter;
if (psldLoopOuter) {
// We've linked it into another SMIMEINFO, AddRef.
psldLoopOuter->AddRef();
// Walk the Outer list to find the end.
while (psldLoopOuter) {
if (! psldLoopOuter->m_psldInner) {
// Found end of list. Tack on original inner list here.
psldLoopOuter->m_psldInner = psldLoopInner;
// Hook up the Outer link
Assert(! psldLoopInner->m_psldOuter);
psldLoopInner->m_psldOuter = psldLoopOuter;
break;
}
psldLoopOuter = psldLoopOuter->m_psldInner;
}
}
// Make sure we bring the encryption layer with us.
if (psiOuter->psldEncrypt) {
Assert(! psiInner->psldEncrypt);
if (! psiInner->psldEncrypt) {
psiInner->psldEncrypt = psiOuter->psldEncrypt;
}
}
// Update the inner layer pointer (in case there was none before)
if (! psiInner->psldInner) {
psiInner->psldInner = psiOuter->psldInner;
}
psiInner->ulLayers += psiOuter->ulLayers;
exit:
return;
}
#endif // KILL_THIS?
#ifndef SMIME_V3
// Bitfield for dw below
#define CAA_SIGNING_TIME 1
#define CAA_SMIME_CAPABILITIES 2
#define CAA_ALL (CAA_SIGNING_TIME | CAA_SMIME_CAPABILITIES)
/***************************************************************************
Name : ConstructAuthAttributes
Purpose : crack open the authenticated attributes blobs and check
if there is a signing time specified. If not, we must
add one. Ditto with the S/Mime Capabilities.
Parameters: pblEncoded -> return blob of encoded Authenticated Attributes
pblAuthAttr -> authenticated attribute blob
data pointer may be replaced
pftSigntime -> signing time
pblSymcaps -> symcaps blob
Returns : HRESULT
Comment : The caller should be careful not to cache the
data pointer within the authenticated attributes blob since
it may be freed in here and replaced with a different one.
***************************************************************************/
static HRESULT ConstructAuthAttributes(BLOB * pblEncoded, BLOB * pblAuthAttr, FILETIME * pftSigntime, BLOB * pblSymcaps)
{
HRESULT hr = S_OK;
ULONG cbData;
ULONG i;
DWORD dw; // bitfield: CAA_SIGNING_TIME, CAA_SMIME_CAPABILITIES
PCRYPT_ATTRIBUTES pattrs = NULL;
BOOL fpattrs = FALSE;
PCRYPT_ATTRIBUTE pattr = NULL;
LPBYTE pbSignTime = NULL;
LPBYTE pbAttr = NULL;
CRYPT_ATTRIBUTES attrs;
CRYPT_ATTR_BLOB valCaps;
CRYPT_ATTR_BLOB valTime;
Assert(pblAuthAttr);
Assert(pftSigntime);
Assert(pblSymcaps);
Assert(pblEncoded);
if ((pblAuthAttr->cbSize > 0) &&
((! HrDecodeObject(pblAuthAttr->pBlobData, pblAuthAttr->cbSize,
szOID_Microsoft_Attribute_Sequence,
0, &cbData, (LPVOID *)&pattrs)) && pattrs)) {
fpattrs = TRUE; // don't forget to free pattrs!
for (i = 0, dw = CAA_ALL; i < pattrs->cAttr; i++) {
if (lstrcmp(pattrs->rgAttr[i].pszObjId, szOID_RSA_signingTime) == 0) {
dw &= ~CAA_SIGNING_TIME;
}
else if (lstrcmp(pattrs->rgAttr[i].pszObjId, szOID_RSA_SMIMECapabilities) == 0) {
dw &= ~CAA_SMIME_CAPABILITIES;
}
}
} else {
// BUGBUG: Should probably report the error if MemAlloc failed. As it sits, there is no
// real harm except that the message will go out without a signing time and symcaps.
dw = CAA_ALL;
pattrs = &attrs;
attrs.cAttr = 0;
attrs.rgAttr = NULL;
}
if (MemAlloc((LPVOID *)&pattr, (pattrs->cAttr + 2) * sizeof(CRYPT_ATTRIBUTE))) {
memcpy(pattr, pattrs->rgAttr, pattrs->cAttr * sizeof(CRYPT_ATTRIBUTE));
pattrs->rgAttr = pattr;
//
// The default answer does not have a signing time in it. We are going
// to create and add a signing time. This may come from either
// a passed in parameter, or from the system.
if (dw & CAA_SIGNING_TIME) {
if ((pftSigntime->dwLowDateTime == 0) &&
(pftSigntime->dwHighDateTime == 0)) {
GetSystemTimeAsFileTime(pftSigntime); // caller sees it now!
}
cbData = 0;
if (CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CHOICE_OF_TIME,
pftSigntime, CRYPT_ENCODE_ALLOC_FLAG,
&CryptEncodeAlloc, &pbSignTime, &cbData)) {
// BUGBUG: Should probably report the error if MemAlloc failed. As it sits, there is no
// real harm except that the message will go out without a signing time and symcaps.
pattr[pattrs->cAttr].pszObjId = szOID_RSA_signingTime;
pattr[pattrs->cAttr].cValue = 1;
pattr[pattrs->cAttr].rgValue = &valTime;
pattr[pattrs->cAttr].rgValue[0].pbData = pbSignTime;
pattr[pattrs->cAttr].rgValue[0].cbData = cbData;
pattrs->cAttr += 1;
}
}
if (dw & CAA_SMIME_CAPABILITIES) {
if (pblSymcaps->cbSize > 0) {
pattr[pattrs->cAttr].pszObjId = szOID_RSA_SMIMECapabilities;
pattr[pattrs->cAttr].cValue = 1;
pattr[pattrs->cAttr].rgValue = &valCaps;
pattr[pattrs->cAttr].rgValue[0].pbData = pblSymcaps->pBlobData;
pattr[pattrs->cAttr].rgValue[0].cbData = pblSymcaps->cbSize;
pattrs->cAttr += 1;
}
}
cbData = 0;
if (CryptEncodeObjectEx(X509_ASN_ENCODING, szOID_Microsoft_Attribute_Sequence,
pattrs, CRYPT_ENCODE_ALLOC_FLAG,
&CryptEncodeAlloc, &pbAttr, &cbData)) {
hr = HrGetLastError();
goto exit;
}
pblEncoded->cbSize = cbData;
pblEncoded->pBlobData = pbAttr;
pbAttr = NULL; // Drop this pointer so that it won't be freed below
} else {
hr = E_OUTOFMEMORY;
}
exit:
SafeMemFree(pattr);
if (fpattrs) {
SafeMemFree(pattrs);
}
SafeMemFree(pbSignTime);
return(hr);
}
#endif // !SMIME_V3
/***************************************************************************
Name : IsSMimeProtocol
Purpose : Test if the protocol type of this root message body is
application/x-pkcs7-signature
Parameters: lpPropSet -> Property set of message
Returns : TRUE if this is an S/MIME protocol message
Comment : Used to differentiate between S/MIME and PGP signatures.
***************************************************************************/
BOOL IsSMimeProtocol(LPMIMEPROPERTYSET lpPropSet) {
PROPVARIANT var;
BOOL fReturn = FALSE;
var.vt = VT_LPSTR;
if (SUCCEEDED(lpPropSet->GetProp(
STR_PAR_PROTOCOL,
0, // [in] DWORD dwFlags,
&var))) {
if (var.pszVal) {
fReturn = (! lstrcmpi(var.pszVal, STR_MIME_APPL_PKCS7SIG)) ||
(! lstrcmpi(var.pszVal, STR_MIME_APPL_PKCS7SIG_1));
SafeMemFree(var.pszVal);
}
}
return(fReturn);
}
/***************************************************************************
Name : DuplicateMemory
Purpose : Allocate a new buffer and copy the old one into it.
Parameters: lpbIn -> Existing buffer
cbIn = size of lpvIn
Returns : new MemAlloc'ed buffer
Comment : Caller is responsible for MemFree'ing the returned buffer.
***************************************************************************/
LPBYTE DuplicateMemory(LPBYTE lpvIn, ULONG cbIn) {
LPBYTE lpbReturn = NULL;
if (MemAlloc((void**)&lpbReturn, cbIn)) {
memcpy(lpbReturn, lpvIn, cbIn);
}
return(lpbReturn);
}
//*************************************************************************
// CSECURITY_LAYER_DATA
//*************************************************************************
///////////////////////////////////////////////////////////////////////////
//
// ctor/dtor
//
CSECURITY_LAYER_DATA::CSECURITY_LAYER_DATA(void)
{
m_cRef = 1;
DOUT("CSECURITY_LAYER_DATA::constructor() %#x -> %d", this, m_cRef);
m_dwMsgEnhancement = MST_NONE;
m_fCertInLayer = FALSE;
m_cSigners = 0;
m_rgSigners = NULL;
m_cEncryptItems = 0;
#ifdef SMIME_V3
m_rgRecipientInfo = NULL;
m_ContentEncryptAlgorithm.pszObjId = NULL;
m_ContentEncryptAlgorithm.Parameters.cbData = 0;
m_ContentEncryptAlgorithm.Parameters.pbData = NULL;
m_pvEncryptAuxInfo = NULL;
m_blobUnprotectAttrs.pbData = NULL;
m_blobUnprotectAttrs.cbData = 0;
m_hstoreEncrypt = NULL;
#else // !SMIME_V3
m_rgEncryptItems = NULL;
#endif // SMIME_V3
m_ulDecValidity = 0;
m_blobDecAlg.cbSize = 0;
m_blobDecAlg.pBlobData = NULL;
m_pccertDecrypt = NULL;
m_hcertstor = NULL;
m_psldInner = NULL;
m_psldOuter = NULL;
}
CSECURITY_LAYER_DATA::~CSECURITY_LAYER_DATA(void)
{
DWORD i;
DWORD i1;
DWORD iSigner;
DOUT("CSECURITY_LAYER_DATA::destructor() %#x -> %d", this, m_cRef);
if (m_psldInner != NULL) {
m_psldInner->Release();
}
if (m_hcertstor != NULL) CertCloseStore(m_hcertstor, 0);
if (m_pccertDecrypt != NULL) CertFreeCertificateContext(m_pccertDecrypt);
SafeMemFree(m_blobDecAlg.pBlobData);
for (iSigner=0; iSigner<m_cSigners; iSigner++) {
if (m_rgSigners[iSigner].pccert != NULL)
CertFreeCertificateContext(m_rgSigners[iSigner].pccert);
SafeMemFree(m_rgSigners[iSigner].blobHashAlg.pBlobData);
SafeMemFree(m_rgSigners[iSigner].blobAuth.pBlobData);
SafeMemFree(m_rgSigners[iSigner].blobUnauth.pBlobData);
#ifdef SMIME_V3
SafeMemFree(m_rgSigners[iSigner].blobReceipt.pBlobData);
SafeMemFree(m_rgSigners[iSigner].blobHash.pBlobData);
#endif // SMIME_V3
}
SafeMemFree(m_rgSigners);
for (i=0; i<m_cEncryptItems; i++) {
#ifdef SMIME_V3
switch (m_rgRecipientInfo[i].dwRecipientChoice) {
case CMSG_KEY_TRANS_RECIPIENT:
if (m_rgRecipientInfo[i].pKeyTrans->KeyEncryptionAlgorithm.pszObjId != 0) {
MemFree(m_rgRecipientInfo[i].pKeyTrans->KeyEncryptionAlgorithm.pszObjId);
MemFree(m_rgRecipientInfo[i].pKeyTrans->KeyEncryptionAlgorithm.Parameters.pbData);
}
if (m_rgRecipientInfo[i].pKeyTrans->pvKeyEncryptionAuxInfo != NULL) {
MemFree(m_rgRecipientInfo[i].pKeyTrans->pvKeyEncryptionAuxInfo);
}
if (m_rgRecipientInfo[i].pKeyTrans->RecipientPublicKey.cbData != 0) {
MemFree(m_rgRecipientInfo[i].pKeyTrans->RecipientPublicKey.pbData);
}
if (m_rgRecipientInfo[i].pKeyTrans->RecipientId.dwIdChoice == CERT_ID_KEY_IDENTIFIER)
{
if (m_rgRecipientInfo[i].pKeyTrans->RecipientId.KeyId.cbData != 0) {
MemFree(m_rgRecipientInfo[i].pKeyTrans->RecipientId.KeyId.pbData);
}
}
else if (m_rgRecipientInfo[i].pKeyTrans->RecipientId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
{
if (m_rgRecipientInfo[i].pKeyTrans->RecipientId.IssuerSerialNumber.Issuer.cbData != 0) {
MemFree(m_rgRecipientInfo[i].pKeyTrans->RecipientId.IssuerSerialNumber.Issuer.pbData);
}
if (m_rgRecipientInfo[i].pKeyTrans->RecipientId.IssuerSerialNumber.SerialNumber.cbData != 0) {
MemFree(m_rgRecipientInfo[i].pKeyTrans->RecipientId.IssuerSerialNumber.SerialNumber.pbData);
}
}
SafeMemFree(m_rgRecipientInfo[i].pKeyTrans);
break;
case CMSG_MAIL_LIST_RECIPIENT:
if (m_rgRecipientInfo[i].pMailList->KeyEncryptionAlgorithm.pszObjId != 0) {
MemFree(m_rgRecipientInfo[i].pMailList->KeyEncryptionAlgorithm.pszObjId);
MemFree(m_rgRecipientInfo[i].pMailList->KeyEncryptionAlgorithm.Parameters.pbData);
}
if (m_rgRecipientInfo[i].pMailList->pvKeyEncryptionAuxInfo != NULL) {
MemFree(m_rgRecipientInfo[i].pMailList->pvKeyEncryptionAuxInfo);
}
if (m_rgRecipientInfo[i].pMailList->pOtherAttr != NULL) {
MemFree(m_rgRecipientInfo[i].pMailList->pOtherAttr->pszObjId);
MemFree(m_rgRecipientInfo[i].pMailList->pOtherAttr->Value.pbData);
}
SafeMemFree(m_rgRecipientInfo[i].pMailList);
break;
case CMSG_KEY_AGREE_RECIPIENT:
if (m_rgRecipientInfo[i].pKeyAgree->KeyEncryptionAlgorithm.pszObjId != 0) {
MemFree(m_rgRecipientInfo[i].pKeyAgree->KeyEncryptionAlgorithm.pszObjId);
MemFree(m_rgRecipientInfo[i].pKeyAgree->KeyEncryptionAlgorithm.Parameters.pbData);
}
if (m_rgRecipientInfo[i].pKeyAgree->pvKeyEncryptionAuxInfo != NULL) {
MemFree(m_rgRecipientInfo[i].pKeyAgree->pvKeyEncryptionAuxInfo);
}
SafeMemFree(m_rgRecipientInfo[i].pKeyAgree);
break;
default:
Assert(FALSE);
break;
}
#else // SMIME_V3
SafeMemFree(m_rgEncryptItems[i].Transport.blobAlg.pBlobData);
switch (m_rgEncryptItems[i].dwTagType) {
case ENCRYPT_ITEM_TRANSPORT:
for (i1=0; i1<m_rgEncryptItems[i].Transport.cCert; i1++) {
CertFreeCertificateContext(m_rgEncryptItems[i].Transport.rgpccert[i1]);
}
SafeMemFree(m_rgEncryptItems[i].Transport.rgpccert);
break;
default:
Assert(FALSE);
break;
}
#endif // SMIME_V3
}
#ifdef SMIME_V3
SafeMemFree(m_rgRecipientInfo);
SafeMemFree(m_pvEncryptAuxInfo);
SafeMemFree(m_ContentEncryptAlgorithm.pszObjId);
SafeMemFree(m_ContentEncryptAlgorithm.Parameters.pbData);
SafeMemFree(m_blobUnprotectAttrs.pbData);
CertCloseStore(m_hstoreEncrypt, 0);
#else // SMIME_V3
SafeMemFree(m_rgEncryptItems);
#endif // SMIME_V3
}
///////////////////////////////////////////////////////////////////////////
//
// IUnknown methods
//
STDMETHODIMP CSECURITY_LAYER_DATA::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) CSECURITY_LAYER_DATA::AddRef(void)
{
DOUT("CSECURITY_LAYER_DATA::AddRef() %#x -> %d", this, m_cRef+1);
InterlockedIncrement((LPLONG)&m_cRef);
return m_cRef;
}
STDMETHODIMP_(ULONG) CSECURITY_LAYER_DATA::Release(void)
{
DOUT("CSECURITY_LAYER_DATA::Release() %#x -> %d", this, m_cRef-1);
if (0 == InterlockedDecrement((LPLONG)&m_cRef)) {
delete this;
return 0;
}
return m_cRef;
}
#ifdef SMIME_V3
/*** HrCopyOID
*
* Description:
* General utility function to make copying of recipient info objects much easier
*/
HRESULT HrCopyOID(LPCSTR psz, LPSTR * ppsz)
{
DWORD cb;
HRESULT hr = S_OK;
cb = strlen(psz) + 1;
CHECKHR(hr = HrAlloc((void **) ppsz, cb));
memcpy(*ppsz, psz, cb);
exit:
return hr;
}
/*** HrCopyCryptDataBlob
*
* Description:
* General utility function to make copying of recipient info objects much easier
*/
HRESULT HrCopyCryptDataBlob(const CRYPT_DATA_BLOB * pblobSrc, PCRYPT_DATA_BLOB pblobDst)
{
HRESULT hr = S_OK;
if (pblobSrc->cbData == 0) {
Assert(pblobDst->pbData == NULL);
Assert(pblobDst->cbData == 0);
goto exit;
}
CHECKHR(hr = HrAlloc((void **) &pblobDst->pbData, pblobSrc->cbData));
memcpy(pblobDst->pbData, pblobSrc->pbData, pblobSrc->cbData);
pblobDst->cbData = pblobSrc->cbData;
exit:
return hr;
}
/*** HrCopyCryptDataBlob
*
* Description:
* General utility function to make copying of recipient info objects much easier
*/
HRESULT HrCopyCryptBitBlob(const CRYPT_BIT_BLOB * pblobSrc, PCRYPT_BIT_BLOB pblobDst)
{
HRESULT hr = S_OK;
if (pblobSrc->cbData == 0) {
Assert(pblobDst->pbData == NULL);
Assert(pblobDst->cbData == 0);
goto exit;
}
CHECKHR(hr = HrAlloc((void **) &pblobDst->pbData, pblobSrc->cbData));
memcpy(pblobDst->pbData, pblobSrc->pbData, pblobSrc->cbData);
pblobDst->cbData = pblobSrc->cbData;
pblobDst->cUnusedBits = pblobSrc->cUnusedBits;
exit:
return hr;
}
HRESULT HrCopyCryptAlgorithm(const CRYPT_ALGORITHM_IDENTIFIER * pAlgSrc,
PCRYPT_ALGORITHM_IDENTIFIER pAlgDst)
{
HRESULT hr = S_OK;
CHECKHR(hr = HrCopyOID(pAlgSrc->pszObjId, &pAlgDst->pszObjId));
if (pAlgSrc->Parameters.cbData != 0) {
CHECKHR(hr = HrCopyCryptDataBlob(&pAlgSrc->Parameters, &pAlgDst->Parameters));
}
exit:
return hr;
}
HRESULT HrCopyCertId(const CERT_ID * pcertidSrc, PCERT_ID pcertidDst)
{
HRESULT hr = S_OK;
pcertidDst->dwIdChoice = pcertidSrc->dwIdChoice;
switch (pcertidSrc->dwIdChoice) {
case CERT_ID_ISSUER_SERIAL_NUMBER:
hr = HrCopyCryptDataBlob(&pcertidSrc->IssuerSerialNumber.Issuer,
&pcertidDst->IssuerSerialNumber.Issuer);
hr = HrCopyCryptDataBlob(&pcertidSrc->IssuerSerialNumber.SerialNumber,
&pcertidDst->IssuerSerialNumber.SerialNumber);
break;
case CERT_ID_KEY_IDENTIFIER:
hr = HrCopyCryptDataBlob(&pcertidSrc->HashId, &pcertidDst->HashId);
break;
case CERT_ID_SHA1_HASH:
hr = HrCopyCryptDataBlob(&pcertidSrc->HashId, &pcertidDst->HashId);
break;
default:
return E_FAIL;
}
return hr;
}
/*** FreeRecipientInfo
*
* Description:
* Free all of the data pointed to by the recipient info as well as the recipient
* info object itself.
*/
void FreeRecipientInfoContent(PCMS_RECIPIENT_INFO pRecipInfo)
{
if (pRecipInfo->pccert != NULL) {
CertFreeCertificateContext(pRecipInfo->pccert);
}
if (pRecipInfo->KeyEncryptionAlgorithm.pszObjId != 0) {
MemFree(pRecipInfo->KeyEncryptionAlgorithm.pszObjId);
MemFree(pRecipInfo->KeyEncryptionAlgorithm.Parameters.pbData);
}
if (pRecipInfo->pvKeyEncryptionAuxInfo != NULL) {
MemFree(pRecipInfo->pvKeyEncryptionAuxInfo);
}
if ((pRecipInfo->dwU1 == CMS_RECIPIENT_INFO_PUBKEY_KEYTRANS) &&
(pRecipInfo->u1.SubjectPublicKey.cbData != 0)) {
MemFree(pRecipInfo->u1.SubjectPublicKey.pbData);
}
if (pRecipInfo->dwU1 == CMS_RECIPIENT_INFO_PUBKEY_PROVIDER) {
Assert(FALSE);
}
if (pRecipInfo->dwU3 == CMS_RECIPIENT_INFO_KEYID_ISSUERSERIAL) {
if (pRecipInfo->u3.IssuerSerial.Issuer.cbData != 0) {
MemFree(pRecipInfo->u3.IssuerSerial.Issuer.pbData);
}
if (pRecipInfo->u3.IssuerSerial.SerialNumber.cbData != 0) {
MemFree(pRecipInfo->u3.IssuerSerial.SerialNumber.pbData);
}
}
if (pRecipInfo->dwU3 == CMS_RECIPIENT_INFO_KEYID_KEY_ID) {
if (pRecipInfo->u3.KeyId.cbData != 0) {
MemFree(pRecipInfo->u3.KeyId.pbData);
}
}
if (pRecipInfo->pOtherAttr != NULL) {
MemFree(pRecipInfo->pOtherAttr->pszObjId);
MemFree(pRecipInfo->pOtherAttr->Value.pbData);
}
return;
}
#endif // SMIME_V3
/* * * END --- SMIME.CPP --- END * * */