/* ** 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 #endif // !MAC #include // 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; ipsldLayers->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; im_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; icbSize = 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; i2rgpRecipientEncryptedKeys, cAgree * sizeof(LPVOID))) { hr = E_OUTOFMEMORY; goto exit; } pAgree->cRecipientEncryptedKeys = cAgree; memset(pAgree->rgpRecipientEncryptedKeys, 0, cAgree * sizeof(LPVOID)); for (i2=i, iAgree = 0; i2rgpRecipientEncryptedKeys[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; icRecipientEncryptedKeys; 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; iSignerpccert; #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; iSignerKeyEncryptionAlgorithm.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; i1AddRef(); 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 * * */