You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4607 lines
152 KiB
4607 lines
152 KiB
/*
|
|
** 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 * * */
|