You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5962 lines
205 KiB
5962 lines
205 KiB
/*
|
|
** s e c u t i l . c p p
|
|
**
|
|
** Purpose:
|
|
** Implementation of a class to wrap around CAPI functionality
|
|
**
|
|
** History
|
|
** 1/12/97: (t-erikne) Recreated after VC ate the file. yum.
|
|
** 1/10/97: (t-erikne) Created.
|
|
**
|
|
** Copyright (C) Microsoft Corp. 1997.
|
|
*/
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Depends on
|
|
//
|
|
|
|
#include "pch.hxx"
|
|
#include "ipab.h"
|
|
#include "secutil.h"
|
|
#include <certs.h>
|
|
#include <imsgcont.h>
|
|
#include "sechtml.h"
|
|
#include <ibodyobj.h>
|
|
#include <wincrypt.h>
|
|
#include <cryptdlg.h>
|
|
#include <capi.h>
|
|
#include "demand.h"
|
|
#include "storecb.h"
|
|
#include "shlwapip.h"
|
|
#include "mailutil.h"
|
|
#include "menuutil.h"
|
|
#include "menures.h"
|
|
#include "mimeolep.h"
|
|
#include "msgprop.h"
|
|
#include "shared.h"
|
|
#include "htmlhelp.h"
|
|
#include "seclabel.h"
|
|
#include "iheader.h"
|
|
#include "browser.h"
|
|
#include "taskutil.h"
|
|
|
|
#define szOID_MSFT_Defaults "1.3.6.1.4.1.311.16.3"
|
|
|
|
#define sz_OEMS_ContIDPrefix "797374"
|
|
|
|
#define PROP_ERROR(prop) (PROP_TYPE(prop.ulPropTag) == PT_ERROR)
|
|
|
|
#define S_DUPLICATE_FOUND MAKE_MAPI_S(0x700)
|
|
|
|
extern INT_PTR CALLBACK CertErrorDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
extern INT_PTR CALLBACK CertWarnDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
|
|
HRESULT HrBuildAndVerifyCerts(IMimeMessageTree * pTree, DWORD * pcCert, PCX509CERT ** prgpccert,
|
|
PCCERT_CONTEXT pccertSender, IImnAccount *pAccount);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Private structures, macros
|
|
//
|
|
|
|
static const TCHAR s_szHTMLMIME[] =
|
|
"Content-Type: text/html\r\n\r\n";
|
|
|
|
// Public constants
|
|
const BYTE c_RC2_40_ALGORITHM_ID[] =
|
|
{0x30, 0x0F, 0x30, 0x0D, 0x06, 0x08, 0x2A, 0x86,
|
|
0x48, 0x86, 0xF7, 0x0D, 0x03, 0x02, 0x02, 0x01,
|
|
0x28};
|
|
const ULONG cbRC2_40_ALGORITHM_ID = 0x11; // Must be 11 hex to match size!
|
|
|
|
#define CONTENTID_SIZE 50
|
|
|
|
///////////////// CAPI Enhancement code
|
|
|
|
#ifdef SMIME_V3
|
|
#define ASN1_ERR_FIRST 0x80093001L
|
|
#define ASN1_ERR_LAST 0x800931FFL
|
|
#endif // SMIME_V3
|
|
|
|
typedef struct tagFilterInfo
|
|
{
|
|
TCHAR *szEmail;
|
|
BOOL fEncryption;
|
|
DWORD dwFlags;
|
|
} ACCTFILTERINFO;
|
|
|
|
CRYPT_ENCODE_PARA CryptEncodeAlloc = {
|
|
sizeof(CRYPT_ENCODE_PARA), CryptAllocFunc, CryptFreeFunc
|
|
};
|
|
|
|
CRYPT_DECODE_PARA CryptDecodeAlloc = {
|
|
sizeof(CRYPT_DECODE_PARA), CryptAllocFunc, CryptFreeFunc
|
|
};
|
|
|
|
#define FILETIME_SECOND 10000000 // 100ns intervals per second
|
|
#define TIME_DELTA_SECONDS 600 // 10 minutes in seconds
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Prototypes
|
|
//
|
|
static int _CompareCertAndSenderEmail(LPMIMEMESSAGE pMsg, IMimeSecurity *pSMime, PCX509CERT pCert);
|
|
static HRESULT _RemoveSecurity(LPMIMEMESSAGE pMsg, HWND hWnd);
|
|
static HRESULT _ValidateAndTrust(HWND hwndOwner, IMimeSecurity *pSMime, IMimeMessage *pMsg);
|
|
static BOOL _IsMaskedBodySecure(LPMIMEMESSAGE pMsg, HBODY hBodyToCheck, DWORD dwMask);
|
|
|
|
#ifdef SMIME_V3
|
|
static HRESULT _HrPrepSecureMsgForSending(HWND hwnd, LPMIMEMESSAGE pMsg, IImnAccount *pAccount, BOOL *pfHaveSenderCert, BOOL *fDontEncryptForSelf, IHeaderSite *pHeaderSite);
|
|
#else
|
|
static HRESULT _HrPrepSecureMsgForSending(HWND hwnd, LPMIMEMESSAGE pMsg, IImnAccount *pAccount, BOOL *pfHaveSenderCert, BOOL *fDontEncryptForSelf);
|
|
#endif // SMIME_V3
|
|
|
|
int GetNumMyCertForAccount(HWND hwnd, IImnAccount * pAccount, BOOL fEncrypt, HCERTSTORE hcMy, PCCERT_CONTEXT * ppcSave);
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Inlines
|
|
//
|
|
|
|
/* _IsMaskedBodySecure:
|
|
**
|
|
** Purpose:
|
|
** Private function to allow the IsSigned, etc queries to work
|
|
** Takes:
|
|
** IN pMsg - message to query
|
|
** IN hBodyToCheck - body to query, HBODY_ROOT is valid
|
|
** IN dwMask - bitmask for MST_ result
|
|
*/
|
|
inline BOOL _IsMaskedBodySecure(LPMIMEMESSAGE pMsg,
|
|
HBODY hBodyToCheck,
|
|
DWORD dwMask)
|
|
{
|
|
return (dwMask & DwGetSecurityOfMessage(pMsg, hBodyToCheck));
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Functions
|
|
//
|
|
|
|
/* 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 && ! (error & 0x80000000))
|
|
hr = error | 0x80070000; // system error
|
|
else
|
|
hr = (HRESULT)error;
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// Here we include a few constants and guids from the WAB API code.
|
|
// This should probably be available somewhere in the WAB headers, but it
|
|
// isn't currently.
|
|
|
|
// From WABAPI code: _mapiprv.h
|
|
// Generic internal entry ID structure
|
|
#pragma warning (disable: 4200)
|
|
typedef struct _MAPIEID {
|
|
BYTE abFlags[4];
|
|
MAPIUID mapiuid;
|
|
BYTE bData[];
|
|
} MAPI_ENTRYID, *LPMAPI_ENTRYID;
|
|
#pragma warning (default: 4200)
|
|
|
|
// From WABAPI code: _entryid.h
|
|
enum _WAB_ENTRYID_TYPE {
|
|
// Must not use 0, this value is invalid.
|
|
WAB_PAB = 1,
|
|
WAB_DEF_DL,
|
|
WAB_DEF_MAILUSER,
|
|
WAB_ONEOFF,
|
|
WAB_ROOT,
|
|
WAB_DISTLIST,
|
|
WAB_CONTAINER,
|
|
WAB_LDAP_CONTAINER,
|
|
WAB_LDAP_MAILUSER
|
|
};
|
|
// From WABAPI code: entryid.c
|
|
static UUID WABGUID = { /* d3ad91c0-9d51-11cf-a4a9-00aa0047faa4 */
|
|
0xd3ad91c0,
|
|
0x9d51,
|
|
0x11cf,
|
|
{0xa4, 0xa9, 0x00, 0xaa, 0x00, 0x47, 0xfa, 0xa4}
|
|
};
|
|
|
|
static UUID MAPIGUID = { /* a41f2b81-a3be-1910-9d6e-00dd010f5402 */
|
|
0xa41f2b81,
|
|
0xa3be,
|
|
0x1910,
|
|
{0x9d, 0x6e, 0x00, 0xdd, 0x01, 0x0f, 0x54, 0x02}
|
|
};
|
|
/***************************************************************************
|
|
|
|
Name : IsWABOneOff
|
|
|
|
Purpose : Is this WAB EntryID a one-off?
|
|
|
|
Parameters: cbEntryID = size of lpEntryID.
|
|
lpEntryID -> entryid to check.
|
|
|
|
Returns : True if this is a WAB one-off entryid
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
BOOL IsWABOneOff(ULONG cbEntryID, LPENTRYID lpEntryID)
|
|
{
|
|
BYTE bType;
|
|
LPMAPI_ENTRYID lpeid;
|
|
LPBYTE lpData1, lpData2, lpData3;
|
|
ULONG cbData1, cbData2;
|
|
LPBYTE lpb;
|
|
|
|
// First check... is it big enough?
|
|
if (cbEntryID < sizeof(MAPI_ENTRYID) + sizeof(bType))
|
|
return(FALSE);
|
|
|
|
lpeid = (LPMAPI_ENTRYID)lpEntryID;
|
|
|
|
// Next check... does it contain our GUID?
|
|
/// MAPI One Off stuff
|
|
if (! memcmp(&lpeid->mapiuid, &MAPIGUID, sizeof(MAPIGUID)))
|
|
{
|
|
lpb = lpeid->bData + sizeof(DWORD);
|
|
bType = WAB_ONEOFF;
|
|
}
|
|
else if (! memcmp(&lpeid->mapiuid, &WABGUID, sizeof(WABGUID)))
|
|
{
|
|
lpb = lpeid->bData;
|
|
bType = *lpb;
|
|
lpb++;
|
|
}
|
|
else
|
|
return(FALSE); // No match
|
|
|
|
switch ((int)bType)
|
|
{
|
|
case WAB_ONEOFF:
|
|
return(TRUE); // This is a WAB One-off
|
|
break;
|
|
|
|
case WAB_PAB:
|
|
case WAB_DEF_DL:
|
|
case WAB_DEF_MAILUSER:
|
|
case WAB_LDAP_CONTAINER:
|
|
case WAB_LDAP_MAILUSER:
|
|
default:
|
|
break; // Not a one-off
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
// enum for ADRENTRY props
|
|
enum {
|
|
irnPR_ENTRYID = 0,
|
|
irnPR_DISPLAY_NAME,
|
|
irnPR_EMAIL_ADDRESS,
|
|
irnPR_OBJECT_TYPE,
|
|
irnMax
|
|
};
|
|
|
|
// enum for Resolve props
|
|
enum {
|
|
irsPR_ENTRYID = 0,
|
|
irsPR_EMAIL_ADDRESS,
|
|
irsPR_CONTACT_EMAIL_ADDRESSES,
|
|
irsPR_CONTACT_ADDRTYPES,
|
|
irsPR_CONTACT_DEFAULT_ADDRESS_INDEX,
|
|
irsPR_DISPLAY_NAME,
|
|
irsPR_OBJECT_TYPE,
|
|
irsPR_USER_X509_CERTIFICATE,
|
|
irsMax
|
|
};
|
|
|
|
SizedSPropTagArray(1, ptaCert) = {1, {PR_USER_X509_CERTIFICATE}};
|
|
|
|
SizedSPropTagArray(1, ptaEntryID) = {1, {PR_ENTRYID}};
|
|
|
|
SizedSPropTagArray(irsMax, ptaResolve) = {irsMax,
|
|
{
|
|
PR_ENTRYID,
|
|
PR_EMAIL_ADDRESS_W,
|
|
PR_CONTACT_EMAIL_ADDRESSES_W,
|
|
PR_CONTACT_ADDRTYPES_W,
|
|
PR_CONTACT_DEFAULT_ADDRESS_INDEX,
|
|
PR_DISPLAY_NAME_W,
|
|
PR_OBJECT_TYPE,
|
|
PR_USER_X509_CERTIFICATE
|
|
}
|
|
};
|
|
|
|
/***************************************************************************
|
|
|
|
Name : HrFindThumbprint
|
|
|
|
Purpose : Find a matching entry with a certificate in the WAB
|
|
|
|
Parameters: pAdrInfo -> ADRINFO structure for this contact
|
|
lpWabal -> WABAL object
|
|
lppspv -> returned data. Caller must WABFreeBuffer the returned pointer.
|
|
Returns : HRESULT, MIME_E_SECURITY_NOCERT if not found
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT HrFindThumbprintInWAB(ADRINFO * pAdrInfo, LPWABAL lpWabal, LPSPropValue * lppspv)
|
|
{
|
|
HRESULT hr = hrSuccess, hrReturn = MIME_E_SECURITY_NOCERT;
|
|
LPADRBOOK lpAdrBook;
|
|
LPADRLIST lpAdrList = NULL;
|
|
ULONG ulObjectType;
|
|
LPMAILUSER lpMailUser = NULL;
|
|
SCODE sc;
|
|
ULONG cProps = 0;
|
|
LPSPropValue ppv = NULL;
|
|
|
|
if (! (lpAdrBook = lpWabal->GetAdrBook())) // Don't release this!
|
|
{
|
|
Assert(lpAdrBook);
|
|
return(MIME_E_SECURITY_NOCERT);
|
|
}
|
|
|
|
if (sc = lpWabal->AllocateBuffer(sizeof(ADRLIST) + 1 * sizeof(ADRENTRY), (LPVOID*)&lpAdrList))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
lpAdrList->cEntries = 1;
|
|
lpAdrList->aEntries[0].ulReserved1 = 0;
|
|
lpAdrList->aEntries[0].cValues = irnMax;
|
|
|
|
// Allocate the prop array for the ADRENTRY
|
|
if (sc = lpWabal->AllocateBuffer(lpAdrList->aEntries[0].cValues * sizeof(SPropValue),
|
|
(LPVOID*)&lpAdrList->aEntries[0].rgPropVals))
|
|
{
|
|
hr = ResultFromScode(sc);
|
|
goto exit;
|
|
}
|
|
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_ENTRYID].ulPropTag = PR_ENTRYID;
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_ENTRYID].Value.bin.cb = 0;
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_ENTRYID].Value.bin.lpb = NULL;
|
|
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_OBJECT_TYPE].ulPropTag = PR_OBJECT_TYPE;
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_OBJECT_TYPE].Value.l = MAPI_MAILUSER;
|
|
|
|
|
|
if (pAdrInfo->lpwszDisplay)
|
|
{
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_DISPLAY_NAME].ulPropTag = PR_DISPLAY_NAME_W;
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_DISPLAY_NAME].Value.lpszW = pAdrInfo->lpwszDisplay;
|
|
}
|
|
else
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_DISPLAY_NAME].ulPropTag = PR_NULL;
|
|
|
|
if (pAdrInfo->lpwszAddress)
|
|
{
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_EMAIL_ADDRESS].ulPropTag = PR_EMAIL_ADDRESS_W;
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_EMAIL_ADDRESS].Value.lpszW = pAdrInfo->lpwszAddress;
|
|
}
|
|
else
|
|
lpAdrList->aEntries[0].rgPropVals[irnPR_EMAIL_ADDRESS].ulPropTag = PR_NULL;
|
|
|
|
hr = lpAdrBook->ResolveName((ULONG)NULL, // hwnd
|
|
WAB_RESOLVE_FIRST_MATCH | WAB_RESOLVE_LOCAL_ONLY | WAB_RESOLVE_ALL_EMAILS |
|
|
WAB_RESOLVE_NO_ONE_OFFS | WAB_RESOLVE_NEED_CERT | WAB_RESOLVE_UNICODE,
|
|
NULL,
|
|
lpAdrList);
|
|
|
|
switch (GetScode(hr))
|
|
{
|
|
case SUCCESS_SUCCESS: // Should be a resolved entry now
|
|
if (lpAdrList->aEntries[0].rgPropVals[irnPR_ENTRYID].ulPropTag == PR_ENTRYID)
|
|
{
|
|
if (! (HR_FAILED(hr = lpAdrBook->OpenEntry(lpAdrList->aEntries[0].rgPropVals[irnPR_ENTRYID].Value.bin.cb,
|
|
(LPENTRYID)lpAdrList->aEntries[0].rgPropVals[irnPR_ENTRYID].Value.bin.lpb,
|
|
NULL,
|
|
MAPI_MODIFY, // ulFlags
|
|
&ulObjectType,
|
|
(LPUNKNOWN *)&(lpMailUser)))))
|
|
{
|
|
|
|
// Got the entry, Get the cert property.
|
|
// NOTE: don't FreeBuffer the ppv. The caller will handle this.
|
|
hr = lpMailUser->GetProps((LPSPropTagArray)&ptaCert, 0, &cProps, &ppv);
|
|
|
|
if (HR_FAILED(hr) || ! cProps || ! ppv || PROP_ERROR(ppv[0]))
|
|
{
|
|
if (ppv)
|
|
lpWabal->FreeBuffer(ppv);
|
|
break;
|
|
}
|
|
|
|
// Got the cert prop
|
|
// Fill in the return prop array with our new prop array
|
|
*lppspv = ppv;
|
|
hrReturn = hrSuccess;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MAPI_E_AMBIGUOUS_RECIP:
|
|
// More than one match. This would be really weird since we specified WAB_RESOLVE_FIRST_MATCH
|
|
Assert(FALSE);
|
|
break;
|
|
|
|
case MAPI_E_NOT_FOUND:
|
|
DOUTL(DOUTL_CRYPT, "ResolveName to find entry with cert failed.");
|
|
// no match with a cert
|
|
break;
|
|
|
|
case MAPI_E_USER_CANCEL:
|
|
hrReturn = hr;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
exit:
|
|
if (lpAdrList)
|
|
{
|
|
for (ULONG iEntry = 0; iEntry < lpAdrList->cEntries; ++iEntry)
|
|
if(lpAdrList->aEntries[iEntry].rgPropVals)
|
|
lpWabal->FreeBuffer(lpAdrList->aEntries[iEntry].rgPropVals);
|
|
lpWabal->FreeBuffer(lpAdrList);
|
|
}
|
|
|
|
if (lpMailUser)
|
|
lpMailUser->Release();
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
BOOL MatchCertEmailAddress(PCCERT_CONTEXT pcCert, LPTSTR szEmailAddress)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
LPSTR szCertEmail = SzGetCertificateEmailAddress(pcCert);
|
|
|
|
if (szCertEmail)
|
|
{
|
|
fRet = !(BOOL(lstrcmpi(szCertEmail, szEmailAddress)));
|
|
MemFree(szCertEmail);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
|
|
// backdoor to avoid having to care about email addresses
|
|
// Hold down the shift key while the Send is happening to
|
|
// skip the address check and use the first one marked default
|
|
fRet = TRUE;
|
|
#endif
|
|
|
|
return(fRet);
|
|
}
|
|
|
|
BOOL CompareCertHash(PCCERT_CONTEXT pCert,
|
|
DWORD dwPropId, PCRYPT_HASH_BLOB pHash )
|
|
{
|
|
BYTE rgbHash[20];
|
|
DWORD cbHash = 20;
|
|
CertGetCertificateContextProperty(pCert,
|
|
dwPropId,
|
|
rgbHash,
|
|
&cbHash);
|
|
if (cbHash == pHash->cbData &&
|
|
memcmp(rgbHash, pHash->pbData, cbHash) == 0) {
|
|
return TRUE;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
HRESULT HrUserSMimeCertCracker(LPBYTE pbIn, DWORD cbIn, HCERTSTORE hCertStoreCA,
|
|
HCERTSTORE hCertStore, BOOL * pfDefault,
|
|
PCCERT_CONTEXT * ppccert, BLOB * pSymCaps)
|
|
{
|
|
DWORD cb;
|
|
DWORD cbCert;
|
|
DWORD cbMaxCert;
|
|
DWORD cbSMimeCaps;
|
|
DWORD cCerts;
|
|
DWORD cSigners;
|
|
DWORD cval;
|
|
DWORD dwDefaults=0;
|
|
DWORD dwNortelAlg;
|
|
BOOL f;
|
|
HCERTSTORE hstoreMem = NULL;
|
|
HCRYPTMSG hmsg;
|
|
HRESULT hr=S_OK;
|
|
ULONG i;
|
|
PCRYPT_ATTRIBUTE pattr;
|
|
PCRYPT_ATTRIBUTE pattrSymCaps = NULL;
|
|
LPBYTE pbCert=NULL;
|
|
LPBYTE pbData;
|
|
LPBYTE pbSMimeCaps;
|
|
PCCERT_CONTEXT pccert;
|
|
PCCERT_CONTEXT pccertReturn = NULL;
|
|
PCMSG_SIGNER_INFO pinfo;
|
|
PCRYPT_RECIPIENT_ID prid = NULL;
|
|
PSMIME_ENC_KEY_PREFERENCE pekp = NULL;
|
|
|
|
hmsg = CryptMsgOpenToDecode(PKCS_7_ASN_ENCODING, 0, 0, NULL,
|
|
NULL, NULL);
|
|
if (hmsg == 0)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (!CryptMsgUpdate(hmsg, pbIn, cbIn, TRUE))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
cb = sizeof(cSigners);
|
|
if (!CryptMsgGetParam(hmsg, CMSG_SIGNER_COUNT_PARAM, 0, &cSigners, &cb) ||
|
|
(cSigners == 0))
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
Assert(cSigners == 1);
|
|
|
|
if (!CryptMsgGetParam(hmsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &cb))
|
|
{
|
|
goto CryptError;
|
|
}
|
|
|
|
pinfo = (PCMSG_SIGNER_INFO) malloc(cb);
|
|
f = CryptMsgGetParam(hmsg, CMSG_SIGNER_INFO_PARAM, 0, pinfo, &cb);
|
|
Assert(f);
|
|
|
|
// M00BUG -- verify signature on message
|
|
|
|
for (i=0; i<pinfo->AuthAttrs.cAttr; i++)
|
|
{
|
|
pattr = &pinfo->AuthAttrs.rgAttr[i];
|
|
if (strcmp(pattr->pszObjId, szOID_RSA_SMIMECapabilities) == 0)
|
|
{
|
|
Assert(pattr->cValue == 1);
|
|
pattrSymCaps = pattr;
|
|
}
|
|
else if (strcmp(pattr->pszObjId, szOID_MSFT_Defaults) == 0)
|
|
{
|
|
Assert(pattr->cValue == 1);
|
|
Assert(pattr->rgValue[0].cbData == 3);
|
|
dwDefaults = pattr->rgValue[0].pbData[2];
|
|
}
|
|
else if (strcmp(pattr->pszObjId, szOID_Microsoft_Encryption_Cert) == 0)
|
|
{
|
|
Assert(pattr->cValue == 1);
|
|
f = CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_Microsoft_Encryption_Cert,
|
|
pattr->rgValue[0].pbData,
|
|
pattr->rgValue[0].cbData,
|
|
CRYPT_DECODE_ALLOC_FLAG, 0,
|
|
(LPVOID *) &prid, &cb);
|
|
Assert(f);
|
|
}
|
|
else if (strcmp(pattr->pszObjId, szOID_SMIME_Encryption_Key_Preference) == 0)
|
|
{
|
|
Assert(pattr->cValue == 1);
|
|
f = CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_SMIME_Encryption_Key_Preference,
|
|
pattr->rgValue[0].pbData,
|
|
pattr->rgValue[0].cbData,
|
|
CRYPT_DECODE_ALLOC_FLAG, 0,
|
|
(LPVOID *) &pekp, &cb);
|
|
Assert(f);
|
|
}
|
|
}
|
|
|
|
if ((prid == NULL) && (pekp == NULL))
|
|
goto Exit;
|
|
|
|
// Enumerate all certs and pack into the structure
|
|
|
|
cbCert = sizeof(cCerts);
|
|
if (!CryptMsgGetParam(hmsg, CMSG_CERT_COUNT_PARAM, 0, &cCerts, &cbCert))
|
|
{
|
|
goto CryptError;
|
|
}
|
|
|
|
cbMaxCert = 0;
|
|
for (i=0; i<cCerts; i++)
|
|
{
|
|
if (!CryptMsgGetParam(hmsg, CMSG_CERT_PARAM, i, NULL, &cbCert))
|
|
{
|
|
goto CryptError;
|
|
}
|
|
if (cbCert > cbMaxCert)
|
|
cbMaxCert = cbCert;
|
|
}
|
|
|
|
pbCert = (LPBYTE) LocalAlloc(0, cbMaxCert);
|
|
if (pbCert == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
hstoreMem = CertOpenStore(CERT_STORE_PROV_MEMORY, X509_ASN_ENCODING, NULL, 0, NULL);
|
|
if (hstoreMem == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Exit;
|
|
}
|
|
|
|
for (i=0; i<cCerts; i++)
|
|
{
|
|
BOOL fFoundEncryptCert;
|
|
|
|
cb = cbMaxCert;
|
|
if (!CryptMsgGetParam(hmsg, CMSG_CERT_PARAM, i, pbCert, &cb))
|
|
{
|
|
goto CryptError;
|
|
}
|
|
|
|
pccert = CertCreateCertificateContext(X509_ASN_ENCODING, pbCert, cb);
|
|
if (pccert == NULL)
|
|
continue;
|
|
|
|
fFoundEncryptCert = FALSE;
|
|
if (pekp != NULL) {
|
|
PCERT_ID pCertId = (PCERT_ID) &pekp->RecipientId;
|
|
|
|
switch (pCertId->dwIdChoice) {
|
|
case CERT_ID_ISSUER_SERIAL_NUMBER:
|
|
if (CertCompareCertificateName(X509_ASN_ENCODING,
|
|
&pccert->pCertInfo->Issuer,
|
|
&pCertId->IssuerSerialNumber.Issuer) &&
|
|
CertCompareIntegerBlob(&pccert->pCertInfo->SerialNumber,
|
|
&pCertId->IssuerSerialNumber.SerialNumber)) {
|
|
fFoundEncryptCert = TRUE;
|
|
//pval[ival].ulPropTag = PR_CERT_KEYEX_CERTIFICATE_BIN;
|
|
}
|
|
break;
|
|
case CERT_ID_KEY_IDENTIFIER:
|
|
if (CompareCertHash(pccert, CERT_KEY_IDENTIFIER_PROP_ID,
|
|
&pCertId->KeyId)) {
|
|
fFoundEncryptCert = TRUE;
|
|
//pval[ival].ulPropTag = PR_CERT_KEYEX_CERTIFICATE_BIN;
|
|
}
|
|
break;
|
|
case CERT_ID_SHA1_HASH:
|
|
if (CompareCertHash(pccert, CERT_SHA1_HASH_PROP_ID,
|
|
&pCertId->HashId)) {
|
|
fFoundEncryptCert = TRUE;
|
|
//pval[ival].ulPropTag = PR_CERT_KEYEX_CERTIFICATE_BIN;
|
|
}
|
|
break;
|
|
default:
|
|
Assert(FALSE);
|
|
}
|
|
}
|
|
else if (prid != NULL) {
|
|
if (CertCompareCertificateName(X509_ASN_ENCODING,
|
|
&pccert->pCertInfo->Issuer,
|
|
&prid->Issuer) &&
|
|
CertCompareIntegerBlob(&pccert->pCertInfo->SerialNumber,
|
|
&prid->SerialNumber)) {
|
|
fFoundEncryptCert = TRUE;
|
|
//pval[ival].ulPropTag = PR_CERT_KEYEX_CERTIFICATE_BIN;
|
|
}
|
|
}
|
|
|
|
if (fFoundEncryptCert)
|
|
{
|
|
pccertReturn = CertDuplicateCertificateContext(pccert);
|
|
CertAddCertificateContextToStore(hCertStore, pccert,
|
|
CERT_STORE_ADD_USE_EXISTING, NULL);
|
|
}
|
|
|
|
CertAddCertificateContextToStore(hstoreMem, pccert,
|
|
CERT_STORE_ADD_USE_EXISTING, NULL);
|
|
CertFreeCertificateContext(pccert);
|
|
}
|
|
|
|
if (pccertReturn == NULL)
|
|
{
|
|
hr = S_FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
HrSaveCACerts(hCertStoreCA, hstoreMem);
|
|
|
|
*ppccert = pccertReturn;
|
|
*pfDefault = dwDefaults;
|
|
if (pattrSymCaps != NULL)
|
|
{
|
|
pSymCaps->pBlobData = (LPBYTE) LocalAlloc(0, pattrSymCaps->rgValue[0].cbData);
|
|
if (pSymCaps->pBlobData != NULL)
|
|
{
|
|
pSymCaps->cbSize = pattrSymCaps->rgValue[0].cbData;
|
|
memcpy(pSymCaps->pBlobData, pattrSymCaps->rgValue[0].pbData,
|
|
pSymCaps->cbSize);
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
|
|
Exit:
|
|
if (pbCert != NULL) LocalFree(pbCert);
|
|
if (prid != NULL) LocalFree(prid);
|
|
if (pekp != NULL) LocalFree(pekp);
|
|
if (pinfo != NULL) LocalFree(pinfo);
|
|
if (hmsg != NULL) CryptMsgClose(hmsg);
|
|
if (hstoreMem != NULL) CertCloseStore(hstoreMem, 0);
|
|
return hr;
|
|
|
|
CryptError:
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
/* HrGetThumbprint:
|
|
**
|
|
** Purpose:
|
|
** Give a wabal, grab a thumbprint from the PR_X509 prop
|
|
** Takes:
|
|
** IN lpWabal - the wabal from which recipients are read
|
|
** IN pAdrInfo - wab entry to query
|
|
** OUT pThumbprint - thumbprint that was found (Caller should MemFree)
|
|
** OUT pSymCaps - symcaps that was found (Caller should MemFree)
|
|
** OUT ftSigningTime - Signing time for cert
|
|
** Returns:
|
|
** SMIME_E_NOCERT if the MAPI cert prop doesn't exist for one of the recips
|
|
** Wabal Layout:
|
|
** PR_X509 = MVBin
|
|
** SBin
|
|
** lpb = tagarr
|
|
** tag
|
|
** tagid = def, trust, thumb
|
|
** data
|
|
** tag
|
|
** tag
|
|
** SBin
|
|
** tagarr
|
|
** tag
|
|
** ...
|
|
*/
|
|
|
|
HRESULT HrGetThumbprint(LPWABAL lpWabal, ADRINFO *pAdrInfo, THUMBBLOB *pThumbprint,
|
|
BLOB * pSymCaps, FILETIME * pftSigningTime)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int iPass;
|
|
ADRINFO rAdrInfo;
|
|
LPMAPIPROP pmp = NULL;
|
|
LPSPropValue ppv = NULL;
|
|
ULONG cCerts;
|
|
ULONG ul, ulDefault = 0;
|
|
HCERTSTORE hCertStore = NULL;
|
|
HCERTSTORE hCertStoreCA = NULL;
|
|
PCCERT_CONTEXT pccert = NULL;
|
|
LPBYTE pbData;
|
|
ULONG cbData;
|
|
LPTSTR pszAddr = NULL;
|
|
|
|
Assert(lpWabal && pAdrInfo);
|
|
pThumbprint->pBlobData = NULL;
|
|
pSymCaps->pBlobData = NULL;
|
|
pSymCaps->cbSize = 0;
|
|
|
|
|
|
pftSigningTime->dwLowDateTime = pftSigningTime->dwHighDateTime = 0;
|
|
|
|
// Find out if this wabal entry is the sender
|
|
if (pAdrInfo->lRecipType == MAPI_ORIG)
|
|
{
|
|
hr = TrapError(MIME_E_SECURITY_NOCERT);
|
|
goto exit;
|
|
}
|
|
|
|
CHECKHR(hr = lpWabal->HrGetPMP(pAdrInfo->cbEID, (LPENTRYID)pAdrInfo->lpbEID, &ul, &pmp));
|
|
CHECKHR(hr = pmp->GetProps((LPSPropTagArray)&ptaCert, 0, &ul, &ppv));
|
|
|
|
if (MAPI_W_ERRORS_RETURNED == hr)
|
|
{
|
|
if (PROP_TYPE(ppv->ulPropTag) == PT_ERROR)
|
|
{
|
|
// the property doesn't exist, so we have no certs
|
|
// for this wabal entry
|
|
|
|
lpWabal->FreeBuffer(ppv);
|
|
ppv = NULL;
|
|
|
|
// Was it a one-off? If so, go look for it in the address book
|
|
if (IsWABOneOff(pAdrInfo->cbEID, (LPENTRYID)pAdrInfo->lpbEID))
|
|
{
|
|
// Look it up
|
|
hr = HrFindThumbprintInWAB(pAdrInfo, lpWabal, &ppv);
|
|
if (FAILED(hr))
|
|
{
|
|
hr = MIME_E_SECURITY_NOCERT; // no cert in wab
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = MIME_E_SECURITY_NOCERT;
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// bad MAPI return
|
|
hr = TrapError(E_FAIL);
|
|
goto exit;
|
|
}
|
|
}
|
|
else if (FAILED(hr) || 1 != ul)
|
|
{
|
|
hr = TrapError(E_FAIL);
|
|
goto exit;
|
|
}
|
|
|
|
// Open Address Book Cert Store
|
|
hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, 0,
|
|
CERT_SYSTEM_STORE_CURRENT_USER, c_szWABCertStore);
|
|
if (!hCertStore)
|
|
{
|
|
// Can't open cert store. No point continuing.
|
|
hr = HrGetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
// Open CA Cert Store
|
|
hCertStoreCA = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, 0,
|
|
CERT_SYSTEM_STORE_CURRENT_USER, c_szCACertStore);
|
|
if (!hCertStoreCA)
|
|
{
|
|
// Can't open cert store. No point continuing.
|
|
hr = HrGetLastError();
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Here is what is going to happen for the next few lines of code. This is
|
|
// drastically different from what the old code here did.
|
|
//
|
|
// Pass #1:
|
|
// Look at each row in the binary structure for the 'default' row.
|
|
// When found extract the cert and verify that it is trusted and valid.
|
|
// If not valid, goto pass #2.
|
|
// If not trusted, -----
|
|
//
|
|
// Now need to loop over the SBinary structures to look at each cert
|
|
|
|
cCerts = ppv->Value.MVbin.cValues;
|
|
for (iPass = 0; (pccert == NULL) && (iPass < 2); iPass++)
|
|
for (ul = 0; ul < cCerts; ul++)
|
|
{
|
|
BOOL fDefault = 0;
|
|
|
|
fDefault = FALSE;
|
|
|
|
// This is the userSMimeCertificate field
|
|
if (ppv->Value.MVbin.lpbin[ul].lpb[0] == CERT_TAG_SMIMECERT)
|
|
{
|
|
hr = HrUserSMimeCertCracker(ppv->Value.MVbin.lpbin[ul].lpb,
|
|
ppv->Value.MVbin.lpbin[ul].cb,
|
|
hCertStoreCA, hCertStore,
|
|
&fDefault, &pccert, pSymCaps);
|
|
if (FAILED(hr))
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// Grab the "default" tag for later testing.
|
|
if (pbData = FindX509CertTag(&ppv->Value.MVbin.lpbin[ul], CERT_TAG_DEFAULT, &cbData))
|
|
{
|
|
memcpy((void*)&fDefault, pbData, min(cbData, sizeof(fDefault)));
|
|
if (!fDefault && (iPass ==0))
|
|
continue;
|
|
}
|
|
|
|
// scan for "thumbprint" tag
|
|
|
|
if (pbData = FindX509CertTag(&ppv->Value.MVbin.lpbin[ul], CERT_TAG_THUMBPRINT, &cbData))
|
|
{
|
|
pThumbprint->cbSize = cbData;
|
|
pThumbprint->pBlobData = pbData;
|
|
|
|
// Find the cert in the store
|
|
pccert = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0,
|
|
CERT_FIND_HASH, (void*)pThumbprint, NULL);
|
|
if (pccert == NULL) // Got the cert context
|
|
{
|
|
pThumbprint->cbSize = 0;
|
|
pThumbprint->pBlobData = NULL;
|
|
continue; // no cert in store, skip this one
|
|
}
|
|
}
|
|
else if (pbData = FindX509CertTag(&ppv->Value.MVbin.lpbin[ul], CERT_TAG_BINCERT, &cbData))
|
|
{
|
|
pccert = CertCreateCertificateContext(X509_ASN_ENCODING, pbData, cbData);
|
|
if (pccert == NULL)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Does the email address of the cert match the email address of the recipient?
|
|
Assert(pAdrInfo->lpwszAddress);
|
|
IF_NULLEXIT(pszAddr = PszToANSI(CP_ACP, pAdrInfo->lpwszAddress));
|
|
|
|
BOOL fSame = MatchCertEmailAddress(pccert, pszAddr);
|
|
SafeMemFree(pszAddr);
|
|
if (fSame)
|
|
{
|
|
DWORD dw = 0;
|
|
HrGetCertKeyUsage(pccert, &dw);
|
|
if(dw == 0xff) // all purposes
|
|
break;
|
|
else if (dw & CERT_KEY_ENCIPHERMENT_KEY_USAGE) // Encryption certificate
|
|
break;
|
|
}
|
|
|
|
CertFreeCertificateContext(pccert);
|
|
pccert = NULL;
|
|
|
|
if (pSymCaps->pBlobData != NULL)
|
|
{
|
|
LocalFree(pSymCaps->pBlobData);
|
|
pSymCaps->pBlobData = NULL;
|
|
pSymCaps->cbSize = 0;
|
|
}
|
|
|
|
// Not the same, go back and try again!
|
|
|
|
pThumbprint->cbSize = 0;
|
|
pThumbprint->pBlobData = NULL;
|
|
|
|
} // for loop over certs
|
|
|
|
if (pccert == NULL)
|
|
{
|
|
hr = MIME_E_SECURITY_NOCERT;
|
|
goto exit;
|
|
}
|
|
hr = hrSuccess;
|
|
|
|
// If we have a match, find other associated tags
|
|
if (pThumbprint->pBlobData)
|
|
{
|
|
if (pbData = FindX509CertTag(&ppv->Value.MVbin.lpbin[ul], CERT_TAG_SYMCAPS, &cbData))
|
|
{
|
|
if (! MemAlloc((LPVOID *)&pSymCaps->pBlobData, cbData))
|
|
{
|
|
hr = TrapError(E_OUTOFMEMORY);
|
|
goto exit;
|
|
}
|
|
pSymCaps->cbSize = cbData;
|
|
memcpy(pSymCaps->pBlobData, pbData, cbData);
|
|
}
|
|
else
|
|
DOUTL(DOUTL_CRYPT, "No symcaps for recipient.");
|
|
|
|
if (pbData = FindX509CertTag(&ppv->Value.MVbin.lpbin[ul], CERT_TAG_SIGNING_TIME, &cbData))
|
|
memcpy(pftSigningTime, &pbData, min(sizeof(FILETIME), cbData));
|
|
else
|
|
DOUTL(DOUTL_CRYPT, "No signing time for recipient.");
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// Make sure the HRESULT is in sync with thumbprint
|
|
if (pccert == NULL)
|
|
Assert(FAILED(hr));
|
|
else
|
|
Assert(SUCCEEDED(hr));
|
|
#endif
|
|
|
|
if (SUCCEEDED(hr) && (pccert != NULL))
|
|
{
|
|
pThumbprint->pBlobData = (LPBYTE) PVGetCertificateParam(pccert, CERT_HASH_PROP_ID, &pThumbprint->cbSize);
|
|
|
|
if (pThumbprint->pBlobData == NULL)
|
|
{
|
|
hr = TrapError(E_OUTOFMEMORY);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (! pThumbprint->pBlobData)
|
|
pThumbprint->cbSize = 0;
|
|
if (ppv)
|
|
lpWabal->FreeBuffer(ppv);
|
|
|
|
ReleaseObj(pmp);
|
|
|
|
if (FAILED(hr) && (pSymCaps != NULL))
|
|
{
|
|
MemFree(pSymCaps->pBlobData);
|
|
pSymCaps->pBlobData = NULL;
|
|
pSymCaps->cbSize = 0;
|
|
}
|
|
|
|
if (pccert)
|
|
CertFreeCertificateContext(pccert);
|
|
|
|
if (hCertStoreCA)
|
|
CertCloseStore(hCertStoreCA, 0);
|
|
if (hCertStore)
|
|
CertCloseStore(hCertStore, 0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/* IsEncrypted:
|
|
**
|
|
** Purpose:
|
|
** Answer the question
|
|
**
|
|
** Takes:
|
|
** IN pMsg - message to query
|
|
** IN hBodyToCheck - body to query, HBODY_ROOT is valid
|
|
** IN fIncludeDescendents - if FALSE, returns with MST_CHILD and
|
|
** MST_SUBMSG don't count
|
|
*/
|
|
BOOL IsEncrypted(LPMIMEMESSAGE pMsg,
|
|
const HBODY hBodyToCheck,
|
|
BOOL fIncludeDescendents)
|
|
{
|
|
return _IsMaskedBodySecure(pMsg, hBodyToCheck,
|
|
fIncludeDescendents ? MST_ENCRYPT_MASK : MST_ENCRYPT_MASK & MST_THIS_MASK);
|
|
}
|
|
|
|
/* IsSigned:
|
|
**
|
|
** Purpose:
|
|
** Answer the question
|
|
**
|
|
** Takes:
|
|
** IN pMsg - message to query
|
|
** IN hBodyToCheck - body to query, HBODY_ROOT is valid
|
|
** IN fIncludeDescendents - if FALSE, returns with MST_CHILD and
|
|
** MST_SUBMSG don't count
|
|
*/
|
|
BOOL IsSigned(LPMIMEMESSAGE pMsg,
|
|
const HBODY hBodyToCheck,
|
|
BOOL fIncludeDescendents)
|
|
{
|
|
return _IsMaskedBodySecure(pMsg, hBodyToCheck,
|
|
fIncludeDescendents ? MST_SIGN_MASK : MST_SIGN_MASK & MST_THIS_MASK);
|
|
}
|
|
|
|
/* DwGetSecurityOfMessage:
|
|
**
|
|
** Purpose:
|
|
** Wrap up the slight nastiness of options
|
|
** Takes:
|
|
** IN pMsg - message to query
|
|
** IN hBodyToCheck - body to query, HBODY_ROOT is valid
|
|
*/
|
|
DWORD DwGetSecurityOfMessage(LPMIMEMESSAGE pMsg,
|
|
const HBODY hBodyToCheck)
|
|
{
|
|
IMimeBody *pBody;
|
|
PROPVARIANT var;
|
|
DWORD dwRet = MST_NONE;
|
|
|
|
Assert(pMsg);
|
|
|
|
if (SUCCEEDED(pMsg->BindToObject(hBodyToCheck, IID_IMimeBody, (void**)&pBody)))
|
|
{
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_TYPE, &var)))
|
|
dwRet = var.ulVal;
|
|
pBody->Release();
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
/* CleanupSECSTATE
|
|
**
|
|
** Purpose:
|
|
** Cleanup the strings allocated by HrGetSecurityState
|
|
** Takes:
|
|
** IN secstate - secstate structure
|
|
*/
|
|
VOID CleanupSECSTATE(SECSTATE *psecstate)
|
|
{
|
|
SafeMemFree(psecstate->szSignerEmail);
|
|
SafeMemFree(psecstate->szSenderEmail);
|
|
}
|
|
|
|
|
|
/* HrHaveAnyMyCerts:
|
|
**
|
|
** Purpose:
|
|
** see if any certificates exist in the CAPI "My" store
|
|
** Returns:
|
|
** S_OK if certificates exist, S_FALSE if the store is empty
|
|
** or does not exist
|
|
*/
|
|
HRESULT HrHaveAnyMyCerts()
|
|
{
|
|
IMimeSecurity *pSMime = NULL;
|
|
HRESULT hr;
|
|
HCAPICERTSTORE hcMy = NULL;
|
|
|
|
CHECKHR(hr = MimeOleCreateSecurity(&pSMime));
|
|
CHECKHR(hr = pSMime->InitNew());
|
|
hcMy = CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
|
|
X509_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_CURRENT_USER, c_szMyCertStore);
|
|
if (hcMy)
|
|
{
|
|
hr = pSMime->EnumCertificates(hcMy, 0, NULL, NULL);
|
|
CertCloseStore(hcMy, 0);
|
|
}
|
|
else
|
|
hr = S_FALSE;
|
|
|
|
exit:
|
|
ReleaseObj(pSMime);
|
|
return hr;
|
|
}
|
|
|
|
/* HandleSecurity:
|
|
**
|
|
** Purpose:
|
|
** Two fold. One, strip the security from the message, so it ends
|
|
** up being rebuilt from the non-secure MIME. Then build the client-
|
|
** side message properties for security.
|
|
**
|
|
** Takes:
|
|
** IN pMsg - message from which to remove security
|
|
*/
|
|
HRESULT HandleSecurity(HWND hwndOwner, LPMIMEMESSAGE pMsg)
|
|
{
|
|
HRESULT hr;
|
|
HWND hWnd = NULL;
|
|
|
|
if(g_pBrowser)
|
|
g_pBrowser->GetWindow(&hWnd);
|
|
if(hWnd == NULL)
|
|
hWnd = hwndOwner;
|
|
|
|
Assert(pMsg);
|
|
hr = _RemoveSecurity(pMsg, hWnd);
|
|
if ((HR_S_NOOP != hr) && (MIME_S_SECURITY_NOOP != hr) && SUCCEEDED(hr))
|
|
{
|
|
IMimeSecurity *pSMime = NULL;
|
|
if (IsSigned(pMsg, FALSE))
|
|
{
|
|
//N2 look at the creation in removsec
|
|
hr = MimeOleCreateSecurity(&pSMime);
|
|
if (SUCCEEDED(hr))
|
|
hr = pSMime->InitNew();
|
|
if (SUCCEEDED(hr))
|
|
hr = _ValidateAndTrust(hwndOwner, pSMime, pMsg);
|
|
ReleaseObj(pSMime);
|
|
}
|
|
}
|
|
else if((hr == OSS_PDU_MISMATCH) || (hr == CRYPT_E_ASN1_BADTAG)) // bug 38394
|
|
{
|
|
AthMessageBoxW(hwndOwner, MAKEINTRESOURCEW(idsAthenaMail),
|
|
MAKEINTRESOURCEW(idsWrongSecHeader), NULL, MB_OK);
|
|
hr = HR_S_NOOP;
|
|
}
|
|
|
|
#if 0
|
|
else if(((hr >= ASN1_ERR_FIRST) && (hr <= ASN1_ERR_LAST)) || (HR_CODE(hr) == ERROR_ACCESS_DENIED))
|
|
hr = MIME_E_SECURITY_LABELACCESSDENIED;
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
#ifdef SMIME_V3
|
|
|
|
// Find secure receipt in decoded message
|
|
HRESULT CheckDecodedForReceipt(LPMIMEMESSAGE pMsg, PSMIME_RECEIPT * ppSecReceipt)
|
|
{
|
|
IMimeBody * pBody = NULL;
|
|
LPBYTE pbData = NULL;
|
|
DWORD cbData, cb;
|
|
LPSTREAM pstmBody = NULL;
|
|
STATSTG statstg;
|
|
HRESULT hr = S_OK;
|
|
HBODY hBody = NULL;
|
|
|
|
if(!IsSecure(pMsg))
|
|
return(E_FAIL);
|
|
|
|
if(FAILED(hr = HrGetInnerLayer(pMsg, &hBody)))
|
|
goto exit;
|
|
|
|
if (FAILED(hr = pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pBody)))
|
|
goto notinit;
|
|
if (FAILED(hr = pBody->GetData(IET_BINARY, &pstmBody)))
|
|
goto notinit;
|
|
if (FAILED(hr = pstmBody->Stat(&statstg,STATFLAG_NONAME)))
|
|
goto notinit;
|
|
|
|
Assert(statstg.cbSize.HighPart == 0);
|
|
|
|
if(statstg.cbSize.LowPart == 0)
|
|
goto notinit;
|
|
if (FAILED(hr = HrAlloc((LPVOID *)&pbData, statstg.cbSize.LowPart)))
|
|
goto notinit;
|
|
if (FAILED(hr = pstmBody->Read(pbData, statstg.cbSize.LowPart, &cbData)))
|
|
{
|
|
notinit:
|
|
hr = MIME_E_SECURITY_NOTINIT;
|
|
goto exit;
|
|
}
|
|
|
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_SMIME_ContentType_Receipt,
|
|
pbData,
|
|
cbData,
|
|
CRYPT_ENCODE_ALLOC_FLAG, &CryptDecodeAlloc,
|
|
ppSecReceipt, &cb))
|
|
{
|
|
hr = MIME_E_SECURITY_RECEIPT_CANTDECODE;
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
SafeMemFree(pbData);
|
|
SafeRelease(pstmBody);
|
|
SafeRelease(pBody);
|
|
return(hr);
|
|
}
|
|
|
|
// Decode a message and find secure receipt
|
|
HRESULT HrFindSecReceipt(LPMIMEMESSAGE pMsg, PSMIME_RECEIPT * ppSecReceipt)
|
|
{
|
|
IMimeSecurity * pSMime = NULL;
|
|
IMimeBody * pBody = NULL;
|
|
LPBYTE pbData = NULL;
|
|
DWORD cbData;
|
|
LPSTREAM pstmBody = NULL;
|
|
STATSTG statstg;
|
|
HRESULT hr = S_OK;
|
|
DWORD cb = 0;
|
|
HWND hWnd = NULL;
|
|
PROPVARIANT var;
|
|
HBODY hBody = NULL;
|
|
|
|
// Need to set correct window for decode
|
|
if(g_pBrowser)
|
|
g_pBrowser->GetWindow(&hWnd);
|
|
|
|
IF_FAILEXIT(hr = HrGetInnerLayer(pMsg, &hBody));
|
|
|
|
IF_FAILEXIT(hr = pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pBody));
|
|
|
|
#ifdef _WIN64
|
|
var.vt = VT_UI8;
|
|
var.pulVal = (ULONG *) hWnd;
|
|
IF_FAILEXIT(hr = pBody->SetOption(OID_SECURITY_HWND_OWNER_64, &var));
|
|
#else
|
|
var.vt = VT_UI4;
|
|
var.ulVal = (DWORD) hWnd;
|
|
IF_FAILEXIT(hr = pBody->SetOption(OID_SECURITY_HWND_OWNER, &var));
|
|
#endif // _WIN64
|
|
SafeRelease(pBody);
|
|
|
|
if (FAILED(hr = MimeOleCreateSecurity(&pSMime)))
|
|
goto notinit;
|
|
if (FAILED(hr = pSMime->InitNew()))
|
|
{
|
|
notinit:
|
|
hr = MIME_E_SECURITY_NOTINIT;
|
|
goto exit;
|
|
}
|
|
|
|
// we handle all hr from DecodeMessage
|
|
IF_FAILEXIT(hr = pSMime->DecodeMessage(pMsg, 0));
|
|
hr = CheckDecodedForReceipt(pMsg, ppSecReceipt);
|
|
|
|
exit:
|
|
SafeRelease(pSMime);
|
|
SafeRelease(pBody);
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT CheckSecReceipt(LPMIMEMESSAGE pMsg)
|
|
{
|
|
PSMIME_RECEIPT pSecReceipt = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = CheckDecodedForReceipt(pMsg, &pSecReceipt);
|
|
SafeMemFree(pSecReceipt);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
#define ROWSET_FETCH 100
|
|
|
|
HRESULT HandleSecReceipt(LPMIMEMESSAGE pMsg, IImnAccount * pAcct, HWND hWnd, TCHAR **ppszSubject, TCHAR **ppszFrom, FILETIME *pftSentTime, FILETIME *pftSigningTime)
|
|
{
|
|
IMimeSecurity2 * pSMIME3 = NULL;
|
|
IMimeBody * pOrgBody = NULL;
|
|
PSMIME_RECEIPT pSecReceipt = NULL;
|
|
HRESULT hr = S_FALSE;
|
|
HRESULT hrVerify = S_OK;
|
|
LPMIMEMESSAGE pOrgMsg = NULL;
|
|
IMessageFolder *pSentItems = NULL;
|
|
HROWSET hRowset=NULL;
|
|
LPMESSAGEINFO pMessage;
|
|
MESSAGEINFO Message={0};
|
|
DWORD i = 0;
|
|
PCCERT_CONTEXT pcSigningCert = NULL;
|
|
THUMBBLOB tbSigner = {0};
|
|
BLOB blSymCaps = {0};
|
|
PROPVARIANT var;
|
|
HBODY hBody = NULL;
|
|
|
|
// bug 80490
|
|
if(pAcct == NULL)
|
|
{
|
|
hr = MIME_E_SECURITY_RECEIPT_CANTFINDSENTITEM;
|
|
goto exit;
|
|
}
|
|
|
|
// IF_FAILEXIT(hr = HrFindSecReceipt(pMsg, &pSecReceipt));
|
|
IF_FAILEXIT(hr = CheckDecodedForReceipt(pMsg, &pSecReceipt));
|
|
|
|
// check that this secure receipt is not from us
|
|
|
|
// Name from sign certificate
|
|
pftSigningTime->dwLowDateTime = 0;
|
|
pftSigningTime->dwHighDateTime = 0;
|
|
|
|
GetSigningCert(pMsg, &pcSigningCert, &tbSigner, &blSymCaps, pftSigningTime);
|
|
if(pcSigningCert)
|
|
{
|
|
HCERTSTORE hMyCertStore = NULL;
|
|
X509CERTRESULT certResult;
|
|
CERTSTATE cs;
|
|
TCHAR *pUserName = NULL;
|
|
|
|
*ppszFrom = SzGetCertificateEmailAddress(pcSigningCert);
|
|
CertFreeCertificateContext(pcSigningCert);
|
|
pcSigningCert = NULL;
|
|
|
|
SafeMemFree(tbSigner.pBlobData);
|
|
|
|
// get certificate for account
|
|
|
|
if (SUCCEEDED(hr = pAcct->GetProp(AP_SMTP_CERTIFICATE, NULL, &tbSigner.cbSize)))
|
|
{
|
|
// we have encryption certificate
|
|
hr = HrAlloc((void**)&tbSigner.pBlobData, tbSigner.cbSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pAcct->GetProp(AP_SMTP_CERTIFICATE, tbSigner.pBlobData, &tbSigner.cbSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING,
|
|
NULL, CERT_SYSTEM_STORE_CURRENT_USER, c_szMyCertStore);
|
|
if (hMyCertStore)
|
|
{
|
|
certResult.cEntries = 1;
|
|
certResult.rgcs = &cs;
|
|
certResult.rgpCert = &pcSigningCert;
|
|
|
|
hr = MimeOleGetCertsFromThumbprints(&tbSigner, &certResult, &hMyCertStore, 1);
|
|
pUserName = SzGetCertificateEmailAddress(pcSigningCert);
|
|
if(!lstrcmpi(pUserName, *ppszFrom))
|
|
hr = MIME_S_RECEIPT_FROMMYSELF;
|
|
|
|
SafeMemFree(pUserName);
|
|
CertCloseStore(hMyCertStore, 0);
|
|
}
|
|
else
|
|
goto notinit;
|
|
}
|
|
SafeMemFree(tbSigner.pBlobData);
|
|
}
|
|
else
|
|
{
|
|
notinit:
|
|
hr = MIME_E_SECURITY_NOTINIT;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(hr == MIME_S_RECEIPT_FROMMYSELF)
|
|
goto exit;
|
|
|
|
// Verification of receipt
|
|
// 1. Try to find Original message
|
|
// 2. If found call pSMIME2->VerifyReceipt
|
|
// 3. Fill all text fields for displaying receipt
|
|
|
|
// Find original message
|
|
// a). Open Sent Item folder for account
|
|
|
|
if (FAILED(hr = TaskUtil_OpenSentItemsFolder(pAcct, &pSentItems)))
|
|
goto NoSentItem;
|
|
// Create a Rowset
|
|
if (FAILED(hr = pSentItems->CreateRowset(IINDEX_PRIMARY, 0, &hRowset)))
|
|
{
|
|
NoSentItem:
|
|
hr = MIME_E_SECURITY_RECEIPT_CANTFINDSENTITEM;
|
|
goto exit;
|
|
}
|
|
|
|
// Walk the Rowset
|
|
hr = MIME_E_SECURITY_RECEIPT_CANTFINDORGMSG;
|
|
while (S_OK == pSentItems->QueryRowset(hRowset, 1, (LPVOID *)&Message, NULL))
|
|
{
|
|
// Messages without request receipt doesn't have pszMSOESRec
|
|
if(Message.pszMSOESRec && (lstrlen(Message.pszMSOESRec) == ((int) pSecReceipt->ContentIdentifier.cbData)))
|
|
{
|
|
if(!memcmp(pSecReceipt->ContentIdentifier.pbData, Message.pszMSOESRec,
|
|
pSecReceipt->ContentIdentifier.cbData))
|
|
{
|
|
// Original message found!!!
|
|
// Need take pMsg and verify receipt
|
|
IF_FAILEXIT(hr = pSentItems->OpenMessage(Message.idMessage, 0, &pOrgMsg, NOSTORECALLBACK));
|
|
IF_FAILEXIT(hr = pOrgMsg->BindToObject(HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pOrgBody));
|
|
IF_FAILEXIT(hr = pOrgBody->QueryInterface(IID_IMimeSecurity2, (LPVOID *) &pSMIME3));
|
|
IF_FAILEXIT(hr = pSMIME3->VerifyReceipt(0, pMsg));
|
|
|
|
// If we are in here then we found original message and verified receipt.
|
|
// Now need to feel all text fields
|
|
|
|
// Subject and Sent from original message
|
|
DWORD cchSize = (lstrlen(Message.pszSubject) + 1);
|
|
if (MemAlloc((LPVOID *)ppszSubject, cchSize * sizeof((*ppszSubject)[0])))
|
|
StrCpyN(*ppszSubject, Message.pszSubject, cchSize);
|
|
|
|
pftSentTime->dwLowDateTime = Message.ftSent.dwLowDateTime;
|
|
pftSentTime->dwHighDateTime = Message.ftSent.dwHighDateTime;
|
|
|
|
if((pftSigningTime->dwLowDateTime == 0) && (pftSigningTime->dwHighDateTime == 0))
|
|
{ // We may have this situation when message was signed, but certificate not included and not in storage.
|
|
SafeRelease(pOrgBody);
|
|
|
|
IF_FAILEXIT(hr = HrGetInnerLayer(pMsg, &hBody));
|
|
|
|
if(pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pOrgBody) == S_OK)
|
|
{
|
|
if (SUCCEEDED(pOrgBody->GetOption(OID_SECURITY_SIGNTIME, &var)))
|
|
{
|
|
Assert(VT_FILETIME == var.vt);
|
|
*pftSigningTime = var.filetime;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = S_OK;
|
|
goto exit;
|
|
}
|
|
}
|
|
pSentItems->FreeRecord(&Message);
|
|
}
|
|
exit:
|
|
SafeRelease(pSMIME3);
|
|
SafeRelease(pOrgBody);
|
|
SafeRelease(pOrgMsg);
|
|
|
|
if(pSentItems)
|
|
pSentItems->FreeRecord(&Message);
|
|
|
|
if(pSentItems && hRowset)
|
|
pSentItems->CloseRowset(&hRowset);
|
|
|
|
SafeRelease(pSentItems);
|
|
SafeMemFree(blSymCaps.pBlobData);
|
|
SafeMemFree(tbSigner.pBlobData);
|
|
if(pcSigningCert)
|
|
CertFreeCertificateContext(pcSigningCert);
|
|
|
|
SafeMemFree(pSecReceipt);
|
|
|
|
return(hr);
|
|
}
|
|
#endif // SMIME_V3
|
|
|
|
HRESULT _RemoveSecurity(LPMIMEMESSAGE pMsg, HWND hWnd)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMimeSecurity *pSMime = NULL;
|
|
IMimeMessageTree *pTree = NULL;
|
|
IMimeBody * pBody = NULL;
|
|
PROPVARIANT var;
|
|
|
|
DWORD dwFlags;
|
|
|
|
//N this is expensive, so _RemoveSecurity should not be called twice
|
|
pMsg->GetFlags(&dwFlags);
|
|
|
|
if (IMF_SECURE & dwFlags)
|
|
{
|
|
IF_FAILEXIT(hr = pMsg->BindToObject(HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pBody));
|
|
|
|
#ifdef _WIN64
|
|
var.vt = VT_UI8;
|
|
var.pulVal = (ULONG *)hWnd;
|
|
IF_FAILEXIT(hr = pBody->SetOption(OID_SECURITY_HWND_OWNER_64, &var));
|
|
#else
|
|
var.vt = VT_UI4;
|
|
var.ulVal = (DWORD) hWnd;
|
|
IF_FAILEXIT(hr = pBody->SetOption(OID_SECURITY_HWND_OWNER, &var));
|
|
#endif // _WIN64
|
|
|
|
CHECKHR(hr = MimeOleCreateSecurity(&pSMime));
|
|
CHECKHR(hr = pSMime->InitNew());
|
|
CHECKHR(hr = pMsg->QueryInterface(IID_IMimeMessageTree, (LPVOID *)&pTree));
|
|
CHECKHR(hr = pSMime->DecodeMessage(pTree, 0));
|
|
}
|
|
else
|
|
hr = HR_S_NOOP;
|
|
|
|
exit:
|
|
ReleaseObj(pBody);
|
|
ReleaseObj(pSMime);
|
|
ReleaseObj(pTree);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT _ValidateAndTrust(HWND hwndOwner, IMimeSecurity *pSMime, IMimeMessage *pMsg)
|
|
{
|
|
ULONG ulTrust = ATHSEC_NOTRUSTUNKNOWN;
|
|
ULONG ulValidity = 0;
|
|
IMimeBody *pBody;
|
|
HRESULT hr;
|
|
HBODY hBody = NULL;
|
|
|
|
Assert(pSMime && pMsg);
|
|
|
|
// Cast the bones and decide if we trust this thing
|
|
|
|
// Athena only supports root-body S/MIME
|
|
if(FAILED(hr = HrGetInnerLayer(pMsg, &hBody)))
|
|
return TrapError(hr);
|
|
|
|
if (SUCCEEDED(hr = pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (void**)&pBody)))
|
|
{
|
|
PROPVARIANT var;
|
|
PCCERT_CONTEXT pcSigningCert;
|
|
|
|
#ifdef _WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING_64, &var)))
|
|
{
|
|
Assert(VT_UI8 == var.vt);
|
|
|
|
pcSigningCert = (PCCERT_CONTEXT)(var.pulVal);
|
|
#else // !_WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING, &var)))
|
|
{
|
|
Assert(VT_UI4 == var.vt);
|
|
|
|
pcSigningCert = (PCCERT_CONTEXT)var.ulVal;
|
|
#endif // _WIN64
|
|
|
|
if (pcSigningCert)
|
|
{
|
|
DWORD dwComputedTrust;
|
|
//N7 trust mask. what if issuer is expired. can't lose this like I do.
|
|
const DWORD dwIgnore =
|
|
// CERT_VALIDITY_BEFORE_START |
|
|
CERT_VALIDITY_AFTER_END |
|
|
CERT_VALIDITY_NO_CRL_FOUND;
|
|
|
|
dwComputedTrust = DwGenerateTrustedChain(hwndOwner, pMsg, pcSigningCert,
|
|
dwIgnore, FALSE, NULL, NULL);
|
|
|
|
// Trust
|
|
ulTrust = ATHSEC_TRUSTED;
|
|
if (dwComputedTrust & CERT_VALIDITY_NO_TRUST_DATA)
|
|
{
|
|
ulTrust |= ATHSEC_NOTRUSTUNKNOWN;
|
|
dwComputedTrust &= ~CERT_VALIDITY_NO_TRUST_DATA;
|
|
}
|
|
if (dwComputedTrust & CERT_VALIDITY_MASK_TRUST)
|
|
{
|
|
ulTrust |= ATHSEC_NOTRUSTNOTTRUSTED;
|
|
dwComputedTrust &= ~CERT_VALIDITY_MASK_TRUST;
|
|
}
|
|
|
|
// Validity
|
|
if (dwComputedTrust & CERT_VALIDITY_CERTIFICATE_REVOKED)
|
|
{
|
|
ulValidity |= ATHSEC_NOTRUSTREVOKED;
|
|
dwComputedTrust &= ~CERT_VALIDITY_CERTIFICATE_REVOKED;
|
|
}
|
|
if (dwComputedTrust & CERT_VALIDITY_NO_CRL_FOUND)
|
|
{
|
|
ulValidity |= ATHSEC_NOTRUSTREVFAIL;
|
|
dwComputedTrust &= ~CERT_VALIDITY_NO_CRL_FOUND;
|
|
}
|
|
|
|
if (dwComputedTrust & CERT_VALIDITY_MASK_VALIDITY)
|
|
{
|
|
ulValidity |= ATHSEC_NOTRUSTOTHER;
|
|
dwComputedTrust &= ~CERT_VALIDITY_MASK_VALIDITY;
|
|
}
|
|
|
|
Assert(dwComputedTrust == ATHSEC_TRUSTED); // Should have removed it all by now
|
|
|
|
//N this could become a helper fn call as part of the trust
|
|
//N provider. Currently trust helpers are nyi.
|
|
if (0 != _CompareCertAndSenderEmail(pMsg, pSMime, pcSigningCert))
|
|
ulValidity |= ATHSEC_NOTRUSTWRONGADDR;
|
|
CertFreeCertificateContext(pcSigningCert);
|
|
}
|
|
else
|
|
// if we don't have a cert then the signing is already
|
|
// in trouble. trust has no meaning
|
|
Assert(!ulValidity);
|
|
}
|
|
|
|
Assert(!(ulTrust & ulValidity)); // no overlap
|
|
hr = (ulTrust | ulValidity) ? S_FALSE : S_OK;
|
|
var.vt = VT_UI4;
|
|
var.ulVal = ulTrust|ulValidity;
|
|
pBody->SetOption(OID_SECURITY_USER_VALIDITY, &var);
|
|
pBody->Release();
|
|
}
|
|
|
|
DOUTL(DOUTL_CRYPT, "SMIME: _ValidateAndTrust returns trust:0x%lX, valid:0x%lX", ulTrust, ulValidity);
|
|
return TrapError(hr);
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : GetSenderEmail
|
|
|
|
Purpose : Get the email address of the sender of the message
|
|
|
|
Parameters: pMsg = IMimeMsg object
|
|
|
|
Returns : MemAlloc'ed string. Caller must MemFree it.
|
|
|
|
***************************************************************************/
|
|
LPTSTR GetSenderEmail(LPMIMEMESSAGE pMsg)
|
|
{
|
|
ADDRESSLIST rAdrList = {0};
|
|
LPTSTR pszReturn = NULL;
|
|
|
|
Assert(pMsg);
|
|
|
|
if (SUCCEEDED(pMsg->GetAddressTypes(IAT_FROM, IAP_EMAIL, &rAdrList)))
|
|
{
|
|
if (rAdrList.cAdrs > 0)
|
|
{
|
|
DWORD cchSize = (lstrlen(rAdrList.prgAdr[0].pszEmail) + 1);
|
|
|
|
Assert(rAdrList.prgAdr[0].pszEmail);
|
|
if (MemAlloc((LPVOID *)&pszReturn, cchSize * sizeof(pszReturn[0])))
|
|
StrCpyN(pszReturn, rAdrList.prgAdr[0].pszEmail, cchSize);
|
|
}
|
|
}
|
|
|
|
g_pMoleAlloc->FreeAddressList(&rAdrList);
|
|
return pszReturn;
|
|
}
|
|
|
|
|
|
/* _CompareCertAndSenderEmail:
|
|
**
|
|
** Returns:
|
|
** 0 if they are equal (case insensitive)
|
|
** //N SECURITY: what about whitespace in the email. mimeOLE strips it?
|
|
** nonzero if unequal
|
|
*/
|
|
int _CompareCertAndSenderEmail(LPMIMEMESSAGE pMsg, IMimeSecurity *pSMime, PCX509CERT pCert)
|
|
{
|
|
int ret = 1;
|
|
PROPVARIANT var;
|
|
HRESULT hr;
|
|
LPTSTR szSenderEmail = NULL;
|
|
|
|
Assert(pMsg && pCert && pSMime);
|
|
|
|
szSenderEmail = GetSenderEmail(pMsg);
|
|
|
|
if (szSenderEmail && SUCCEEDED(hr = pSMime->GetCertData(pCert, CDID_EMAIL, &var)))
|
|
{
|
|
Assert(VT_LPSTR == var.vt);
|
|
ret = lstrcmpi(szSenderEmail, var.pszVal);
|
|
MemFree(var.pszVal);
|
|
}
|
|
|
|
SafeMemFree(szSenderEmail);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : HrInitSecurityOptions
|
|
|
|
Purpose : Set some basic security options on the message.
|
|
|
|
Parameters: pMsg = IMimeMsg object
|
|
ulSecurityType = SMIME security type:
|
|
MST_THIS_SIGN
|
|
MST_THIS_ENCRYPT
|
|
MST_CLASS_SMIME_V1
|
|
MST_THIS_BLOBSIGN
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : Sets the security type option on all messages
|
|
Sets hash algorithm only if we are signing.
|
|
|
|
***************************************************************************/
|
|
HRESULT HrInitSecurityOptions(LPMIMEMESSAGE pMsg, ULONG ulSecurityType)
|
|
{
|
|
HRESULT hr;
|
|
IMimeBody *pBody = NULL;
|
|
PROPVARIANT var;
|
|
|
|
hr = pMsg->BindToObject(HBODY_ROOT, IID_IMimeBody, (void**)&pBody);
|
|
if (FAILED(hr))
|
|
goto exit;
|
|
|
|
var.vt = VT_UI4;
|
|
var.ulVal = ulSecurityType;
|
|
hr = pBody->SetOption(OID_SECURITY_TYPE, &var);
|
|
if (FAILED(hr))
|
|
goto exit;
|
|
|
|
if (ulSecurityType & MST_SIGN_MASK)
|
|
{
|
|
// Hack! This is the ALOGORITHM ID for SHA1, our only supported signing algorithm
|
|
BYTE rgbHash[] = {0x30, 0x09, 0x30, 0x07, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A};
|
|
ULONG cbHash = sizeof(rgbHash);
|
|
|
|
var.vt = VT_BLOB;
|
|
var.blob.cbSize = cbHash;
|
|
var.blob.pBlobData = rgbHash;
|
|
hr = pBody->SetOption(OID_SECURITY_ALG_HASH, &var);
|
|
}
|
|
|
|
if(ulSecurityType == MST_THIS_SIGN)
|
|
{
|
|
var.vt = VT_BOOL;
|
|
var.boolVal = TRUE;
|
|
hr = pMsg->SetOption(OID_SAVEBODY_KEEPBOUNDARY, &var);
|
|
}
|
|
|
|
exit:
|
|
ReleaseObj(pBody);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Get inner layer for using with multisign messages
|
|
HRESULT HrGetInnerLayer(LPMIMEMESSAGE pMsg, HBODY *phBody)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HBODY hBody = NULL;
|
|
BOOL fWrapped = FALSE;
|
|
IMimeBody *pBody = NULL;
|
|
|
|
Assert(pMsg);
|
|
hr = pMsg->GetBody(IBL_ROOT, NULL, &hBody);
|
|
if(FAILED(hr))
|
|
return(hr);
|
|
|
|
do
|
|
{
|
|
if (SUCCEEDED(hr = pMsg->BindToObject(hBody, IID_IMimeBody, (void **)&pBody)))
|
|
{
|
|
fWrapped = (pBody->IsContentType(STR_CNT_MULTIPART, "y-security") == S_OK);
|
|
|
|
if(phBody)
|
|
*phBody = hBody;
|
|
|
|
SafeRelease(pBody);
|
|
|
|
if (fWrapped)
|
|
{
|
|
hr = pMsg->GetBody(IBL_FIRST, hBody, &hBody);
|
|
Assert(SUCCEEDED(hr));
|
|
if (FAILED(hr))
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
|
|
}while(fWrapped && hBody);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT HrGetSecurityState(LPMIMEMESSAGE pMsg, SECSTATE *pSecState, HBODY *phBody)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMimeBody *pBody = NULL;
|
|
PROPVARIANT var;
|
|
BOOL fWrapped = FALSE;
|
|
|
|
|
|
pSecState->szSignerEmail = NULL;
|
|
pSecState->szSenderEmail = NULL;
|
|
HBODY hBody;
|
|
|
|
hr = pMsg->GetBody(IBL_ROOT, NULL, &hBody);
|
|
if(FAILED(hr))
|
|
return(hr);
|
|
|
|
do
|
|
{
|
|
if (SUCCEEDED(hr = pMsg->BindToObject(hBody, IID_IMimeBody, (void **)&pBody)))
|
|
{
|
|
SafeMemFree(pSecState->szSignerEmail);
|
|
SafeMemFree(pSecState->szSenderEmail);
|
|
|
|
pSecState->type = SUCCEEDED(pBody->GetOption(OID_SECURITY_TYPE, &var))
|
|
? var.ulVal
|
|
: MST_NONE;
|
|
|
|
pSecState->szSenderEmail = GetSenderEmail(pMsg);
|
|
|
|
if (MST_NONE != pSecState->type)
|
|
{
|
|
pSecState->user_validity = SUCCEEDED(pBody->GetOption(OID_SECURITY_USER_VALIDITY, &var))
|
|
? var.ulVal
|
|
: 0;
|
|
|
|
pSecState->ro_msg_validity = SUCCEEDED(pBody->GetOption(OID_SECURITY_RO_MSG_VALIDITY, &var))
|
|
? var.ulVal
|
|
: MSV_OK;
|
|
|
|
#ifdef _WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING_64, &var)))
|
|
{
|
|
pSecState->fHaveCert = (NULL != (PCCERT_CONTEXT)(var.pulVal));
|
|
|
|
if (pSecState->fHaveCert)
|
|
{
|
|
pSecState->szSignerEmail = SzGetCertificateEmailAddress((PCCERT_CONTEXT)(var.pulVal));
|
|
CertFreeCertificateContext((PCCERT_CONTEXT)(var.pulVal));
|
|
}
|
|
}
|
|
#else //!_WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING, &var)))
|
|
{
|
|
pSecState->fHaveCert = (NULL != ((PCCERT_CONTEXT)var.ulVal));
|
|
|
|
if (pSecState->fHaveCert)
|
|
{
|
|
pSecState->szSignerEmail = SzGetCertificateEmailAddress((PCCERT_CONTEXT)var.ulVal);
|
|
CertFreeCertificateContext((PCCERT_CONTEXT)var.ulVal);
|
|
}
|
|
}
|
|
#endif // _WIN64
|
|
else
|
|
pSecState->fHaveCert = FALSE;
|
|
}
|
|
fWrapped = (pBody->IsContentType(STR_CNT_MULTIPART, "y-security") == S_OK);
|
|
|
|
if(phBody)
|
|
*phBody = hBody;
|
|
|
|
SafeRelease(pBody);
|
|
|
|
if (fWrapped)
|
|
{
|
|
hr = pMsg->GetBody(IBL_FIRST, hBody, &hBody);
|
|
Assert(SUCCEEDED(hr));
|
|
if (FAILED(hr))
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
break;
|
|
|
|
if(IsSigned(pSecState->type) && !IsSignTrusted(pSecState))
|
|
break;
|
|
|
|
if(IsEncrypted(pSecState->type) && !IsEncryptionOK(pSecState))
|
|
break;
|
|
|
|
}while(fWrapped && hBody);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : CheckAndFixMissingCert
|
|
|
|
Purpose : Check to see if we can locate the certs for the missing
|
|
entries.
|
|
|
|
Parameters: hwnd = window handle
|
|
pAdrTable = Address Table object
|
|
pAccount = sending account
|
|
|
|
Returns : TRUE if we able to find and fix at least one missing cert.
|
|
FALSE if there was nothing we could do.
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
BOOL CheckAndFixMissingMeCert(HWND hwnd, IMimeAddressTable *pAdrTable, IImnAccount *pAccount)
|
|
{
|
|
IMimeEnumAddressTypes *pEnum = NULL;
|
|
ADDRESSPROPS apAddress = {0};
|
|
ADDRESSPROPS apModify = {0};
|
|
TCHAR szAcctEmailAddress[CCHMAX_EMAIL_ADDRESS + 1] = "";
|
|
HRESULT hr;
|
|
THUMBBLOB tbSender = {0, 0};
|
|
BOOL fRet = FALSE;
|
|
PCCERT_CONTEXT pcCert = NULL;
|
|
|
|
Assert(pAdrTable && pAccount);
|
|
|
|
pAccount->GetPropSz(AP_SMTP_EMAIL_ADDRESS, szAcctEmailAddress, sizeof(szAcctEmailAddress));
|
|
|
|
if (FAILED(pAdrTable->EnumTypes(IAT_TO | IAT_CC | IAT_BCC, IAP_HANDLE | IAP_EMAIL | IAP_ADRTYPE | IAP_CERTSTATE | IAP_FRIENDLY, &pEnum)))
|
|
return(FALSE);
|
|
|
|
while (S_OK == pEnum->Next(1, &apAddress, NULL))
|
|
{
|
|
if (CERTIFICATE_NOT_PRESENT == apAddress.certstate || CERTIFICATE_NOPRINT == apAddress.certstate)
|
|
{
|
|
// Don't have this recipient's cert
|
|
// Is this recipient ME?
|
|
if (! lstrcmpi(apAddress.pszEmail, szAcctEmailAddress))
|
|
{
|
|
// Yes, this is me. Get my cert and put it in here.
|
|
// Do I have a cert in the account?
|
|
hr = pAccount->GetProp(AP_SMTP_CERTIFICATE, NULL, &apModify.tbEncryption.cbSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (SUCCEEDED(HrAlloc((void**)&apModify.tbEncryption.pBlobData, apModify.tbEncryption.cbSize)))
|
|
pAccount->GetProp(AP_SMTP_CERTIFICATE, apModify.tbEncryption.pBlobData, &apModify.tbEncryption.cbSize);
|
|
}
|
|
else
|
|
{
|
|
// No, go get one
|
|
hr = _HrFindMyCertForAccount(hwnd, &pcCert, pAccount, FALSE);
|
|
if (SUCCEEDED(hr) && pcCert)
|
|
{
|
|
// Get the thumbprint
|
|
apModify.tbEncryption.pBlobData = (BYTE *)PVGetCertificateParam(pcCert, CERT_HASH_PROP_ID, &apModify.tbEncryption.cbSize);
|
|
CertFreeCertificateContext(pcCert);
|
|
pcCert = NULL;
|
|
}
|
|
}
|
|
|
|
// OK, do I finally have a cert?
|
|
if (apModify.tbEncryption.pBlobData && apModify.tbEncryption.cbSize)
|
|
{
|
|
apModify.dwProps = IAP_ENCRYPTION_PRINT;
|
|
if (SUCCEEDED(hr = pAdrTable->SetProps(apAddress.hAddress, &apModify)))
|
|
fRet = TRUE;
|
|
}
|
|
SafeMemFree(apModify.tbEncryption.pBlobData);
|
|
apModify.tbEncryption.cbSize = 0;
|
|
}
|
|
}
|
|
g_pMoleAlloc->FreeAddressProps(&apAddress);
|
|
}
|
|
|
|
ReleaseObj(pEnum);
|
|
return(fRet);
|
|
}
|
|
|
|
HRESULT SendSecureMailToOutBox(IStoreCallback *pStoreCB, LPMIMEMESSAGE pMsg, BOOL fSendImmediate, BOOL fNoUI, BOOL fMail, IHeaderSite *pHeaderSite)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fNoErrorUI;
|
|
BOOL fContLoop;
|
|
BOOL fHaveSender;
|
|
BOOL fAllowTryAgain;
|
|
PROPVARIANT var;
|
|
BOOL fDontEncryptForSelf = !!DwGetOption(OPT_NO_SELF_ENCRYPT);
|
|
HWND hwndOwner = 0;
|
|
IImnAccount *pAccount = NULL;
|
|
CERTERRPARAM CertErrParam = {0};
|
|
|
|
Assert(IsSecure(pMsg));
|
|
|
|
pStoreCB->GetParentWindow(0, &hwndOwner);
|
|
AssertSz(hwndOwner, "How did we not get an hwnd???");
|
|
|
|
var.vt = VT_LPSTR;
|
|
hr = pMsg->GetProp(PIDTOSTR(PID_ATT_ACCOUNTID), NOFLAGS, &var);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = g_pAcctMan->FindAccount(AP_ACCOUNT_ID, var.pszVal, &pAccount);
|
|
|
|
SafeMemFree(var.pszVal);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
if (FAILED(hr = _HrPrepSecureMsgForSending(hwndOwner, pMsg, pAccount, &fHaveSender, &fDontEncryptForSelf, pHeaderSite)))
|
|
{
|
|
SafeRelease(pAccount);
|
|
return hr;
|
|
}
|
|
|
|
var.ulVal = 0;
|
|
hr = pMsg->GetOption(OID_SECURITY_ENCODE_FLAGS, &var);
|
|
|
|
if (fHaveSender)
|
|
{
|
|
var.ulVal |= SEF_SENDERSCERTPROVIDED;
|
|
|
|
hr = pMsg->SetOption(OID_SECURITY_ENCODE_FLAGS, &var);
|
|
}
|
|
|
|
fNoErrorUI = FALSE;
|
|
fContLoop = TRUE;
|
|
fAllowTryAgain = TRUE;
|
|
do
|
|
{
|
|
hr = SendMailToOutBox(pStoreCB, pMsg, fSendImmediate, fNoUI, fMail);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
fContLoop = FALSE;
|
|
break;
|
|
}
|
|
else if ((MIME_E_SECURITY_CERTERROR == hr) || (MIME_E_SECURITY_NOCERT == hr))
|
|
{
|
|
IMimeAddressTable *pAdrTable;
|
|
|
|
if (SUCCEEDED(hr = pMsg->GetAddressTable(&pAdrTable)))
|
|
{
|
|
// First off, are we sending to ourselves? If so, find our cert, make sure
|
|
// there's a cert associated with the account and then add it to the address
|
|
// table.
|
|
if (fAllowTryAgain && CheckAndFixMissingMeCert(hwndOwner,
|
|
pAdrTable,
|
|
pAccount))
|
|
{
|
|
// Try again, we found at least one missing cert.
|
|
fContLoop = TRUE;
|
|
}
|
|
else
|
|
{
|
|
// Didn't get them all, tell the user.
|
|
CertErrParam.pAdrTable = pAdrTable;
|
|
CertErrParam.fForceEncryption = FALSE;
|
|
if (pHeaderSite != NULL)
|
|
{
|
|
if(pHeaderSite->IsForceEncryption() == S_OK)
|
|
CertErrParam.fForceEncryption = TRUE;
|
|
}
|
|
|
|
|
|
if(IDOK == DialogBoxParam(g_hLocRes, MAKEINTRESOURCE(iddSecCerificateErr),
|
|
hwndOwner, CertErrorDlgProc, (LPARAM)(&CertErrParam)))
|
|
{
|
|
ULONG ulSecurityType = MST_CLASS_SMIME_V1;
|
|
|
|
if (IsSigned(pMsg, TRUE))
|
|
ulSecurityType |= ((DwGetOption(OPT_OPAQUE_SIGN)) ? MST_THIS_BLOBSIGN : MST_THIS_SIGN);
|
|
else
|
|
ulSecurityType &= ~((DwGetOption(OPT_OPAQUE_SIGN)) ? MST_THIS_BLOBSIGN : MST_THIS_SIGN);
|
|
|
|
|
|
ulSecurityType &= ~MST_THIS_ENCRYPT;
|
|
hr = HrInitSecurityOptions(pMsg, ulSecurityType);
|
|
fContLoop = TRUE;
|
|
}
|
|
else
|
|
fContLoop = FALSE; //N work item: allow users to send anyway
|
|
|
|
fNoErrorUI = TRUE;
|
|
}
|
|
|
|
pAdrTable->Release();
|
|
fAllowTryAgain = FALSE;
|
|
}
|
|
}
|
|
else if (MIME_E_SECURITY_ENCRYPTNOSENDERCERT == hr)
|
|
{
|
|
// We may have situations in here Signed & Crypted, but no certificate (bug 60056)
|
|
if (IsSigned(pMsg, TRUE))
|
|
{
|
|
|
|
AthMessageBoxW(hwndOwner, MAKEINTRESOURCEW(idsSecurityWarning), MAKEINTRESOURCEW(idsErrSecurityNoSigningCert), NULL, MB_OK | MB_ICONEXCLAMATION);
|
|
fNoErrorUI = TRUE;
|
|
fContLoop = FALSE;
|
|
}
|
|
// find out if user doesn't care
|
|
else if (fDontEncryptForSelf || (IDYES == AthMessageBoxW(hwndOwner,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(idsWrnSecurityNoCertForEnc), NULL,
|
|
MB_YESNO|MB_DEFBUTTON2|MB_ICONWARNING|MB_SETFOREGROUND)))
|
|
{
|
|
var.ulVal |= SEF_ENCRYPTWITHNOSENDERCERT;
|
|
|
|
// abort on failure to avoid extra looping
|
|
if (FAILED(hr = pMsg->SetOption(OID_SECURITY_ENCODE_FLAGS, &var)))
|
|
fContLoop = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fNoErrorUI = TRUE;
|
|
fContLoop = FALSE;
|
|
}
|
|
}
|
|
else if (CRYPT_E_NO_KEY_PROPERTY == hr)
|
|
{
|
|
// no choice for this one
|
|
AthMessageBoxW(hwndOwner,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(idsErrSecurityNoPrivateKey), NULL,
|
|
MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
|
|
fContLoop = FALSE;
|
|
fNoErrorUI = TRUE;
|
|
}
|
|
else if (E_ACCESSDENIED == hr)
|
|
{
|
|
// no choice for this one
|
|
AthMessageBoxW(hwndOwner,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(idsErrSecurityAccessDenied), NULL,
|
|
MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
|
|
fContLoop = FALSE;
|
|
fNoErrorUI = TRUE;
|
|
}
|
|
else
|
|
// unknown error
|
|
fContLoop = FALSE;
|
|
} while (fContLoop);
|
|
|
|
if (fNoErrorUI)
|
|
// return the recognized error code
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
ReleaseObj(pAccount);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT HrBuildAndVerifyCerts(HWND hwnd, IMimeMessageTree * pTree, DWORD * pcCert,
|
|
PCX509CERT ** prgpccert, PCCERT_CONTEXT pccertSender, IImnAccount *pAccount, IHeaderSite *pHeaderSite)
|
|
{
|
|
ADDRESSPROPS apEntry;
|
|
ADDRESSPROPS apModify;
|
|
DWORD cCerts;
|
|
DWORD cPrints = 0;
|
|
DWORD dw;
|
|
HRESULT hr;
|
|
DWORD i;
|
|
IMimeAddressTable * pAdrTbl = NULL;
|
|
IMimeEnumAddressTypes * pAdrEnum = NULL;
|
|
HCERTSTORE rgCertStore[2];
|
|
PCX509CERT * rgpccert = NULL;
|
|
CERTERRPARAM CertErrParam = {0};
|
|
|
|
if (SUCCEEDED(hr = pTree->BindToObject(HBODY_ROOT, IID_IMimeAddressTable,
|
|
(void**)&pAdrTbl))) {
|
|
hr = pAdrTbl->EnumTypes(IAT_TO | IAT_CC | IAT_BCC | IAT_SENDER,
|
|
IAP_HANDLE | IAP_ADRTYPE | IAP_SIGNING_PRINT |
|
|
IAP_ENCRYPTION_PRINT | IAP_CERTSTATE, &pAdrEnum);
|
|
}
|
|
|
|
|
|
pAdrEnum->Count(&cCerts);
|
|
if (cCerts == 0) {
|
|
return MIME_S_SECURITY_NOOP;
|
|
}
|
|
|
|
if (!MemAlloc((LPVOID *) &rgpccert, sizeof(PCX509CERT)*(cCerts+1))) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
memset(rgpccert, 0, sizeof(CERTSTATE)*(cCerts+1));
|
|
|
|
rgCertStore[1] = CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
|
|
X509_ASN_ENCODING, NULL,
|
|
CERT_STORE_READONLY_FLAG|CERT_SYSTEM_STORE_CURRENT_USER, c_szMyCertStore);
|
|
if (rgCertStore[1] == NULL) {
|
|
hr = MIME_S_SECURITY_ERROROCCURED;
|
|
goto Exit;
|
|
}
|
|
rgCertStore[0] = CertOpenStore(CERT_STORE_PROV_SYSTEM_A,
|
|
X509_ASN_ENCODING, NULL,
|
|
CERT_STORE_READONLY_FLAG|CERT_SYSTEM_STORE_CURRENT_USER, c_szWABCertStore);
|
|
if (rgCertStore[0] == NULL) {
|
|
hr = MIME_S_SECURITY_ERROROCCURED;
|
|
goto Exit;
|
|
}
|
|
|
|
pAdrEnum->Reset();
|
|
apModify.dwProps = IAP_CERTSTATE;
|
|
|
|
while (pAdrEnum->Next(1, &apEntry, NULL) == S_OK) {
|
|
Assert((apEntry.tbEncryption.pBlobData && apEntry.tbEncryption.cbSize) ||
|
|
(!apEntry.tbEncryption.pBlobData && !apEntry.tbEncryption.cbSize));
|
|
|
|
|
|
if (apEntry.tbEncryption.cbSize) {
|
|
CRYPT_DIGEST_BLOB blob;
|
|
|
|
// Make an assumption
|
|
apModify.certstate = CERTIFICATE_INVALID;
|
|
|
|
// we need to null out the thumbprint flag so print doesn't get
|
|
// freed below
|
|
blob.pbData = apEntry.tbEncryption.pBlobData;
|
|
blob.cbData = apEntry.tbEncryption.cbSize;
|
|
for (i=0; i<2; i++) {
|
|
rgpccert[cPrints] = CertFindCertificateInStore(rgCertStore[i],
|
|
X509_ASN_ENCODING, 0,
|
|
CERT_FIND_HASH,
|
|
(LPVOID) &blob, NULL);
|
|
if (rgpccert[cPrints] == NULL)
|
|
continue;
|
|
|
|
HrGetCertKeyUsage(rgpccert[cPrints], &dw);
|
|
if (!(dw & CERT_KEY_ENCIPHERMENT_KEY_USAGE)) {
|
|
CertFreeCertificateContext(rgpccert[cPrints]);
|
|
rgpccert[cPrints] = NULL;
|
|
continue;
|
|
}
|
|
|
|
|
|
dw = DwGenerateTrustedChain(hwnd, NULL, rgpccert[cPrints],
|
|
CERT_VALIDITY_NO_CRL_FOUND, TRUE, NULL, NULL);
|
|
if (dw & CERT_VALIDITY_NO_TRUST_DATA) {
|
|
apModify.certstate = CERTIFICATE_NOT_TRUSTED;
|
|
}
|
|
else if (dw & CERT_VALIDITY_NO_ISSUER_CERT_FOUND) {
|
|
apModify.certstate = CERTIFICATE_MISSING_ISSUER;
|
|
}
|
|
else if (dw & (CERT_VALIDITY_BEFORE_START | CERT_VALIDITY_AFTER_END)) {
|
|
apModify.certstate = CERTIFICATE_EXPIRED;
|
|
}
|
|
else if (dw & CERT_VALIDITY_CERTIFICATE_REVOKED) {
|
|
apModify.certstate = CERTIFICATE_CRL_LISTED;
|
|
}
|
|
else if (dw & CERT_VALIDITY_MASK_TRUST) {
|
|
apModify.certstate = CERTIFICATE_NOT_TRUSTED;
|
|
}
|
|
else if (dw & CERT_VALIDITY_MASK_VALIDITY) {
|
|
apModify.certstate = CERTIFICATE_INVALID;
|
|
}
|
|
|
|
// Check label
|
|
hr = S_OK;
|
|
if(pHeaderSite != NULL)
|
|
{
|
|
PSMIME_SECURITY_LABEL plabel = NULL;
|
|
|
|
hr = pHeaderSite->GetLabelFromNote(&plabel);
|
|
if(plabel)
|
|
{
|
|
hr = HrValidateLabelRecipCert(plabel, hwnd, rgpccert[cPrints]);
|
|
if(FAILED(hr))
|
|
apModify.certstate = CERTIFICATE_INVALID;
|
|
}
|
|
else
|
|
hr = S_OK; // ignore any error
|
|
|
|
}
|
|
|
|
if (dw || FAILED(hr))
|
|
{
|
|
CertFreeCertificateContext(rgpccert[cPrints]);
|
|
rgpccert[cPrints] = NULL;
|
|
continue;
|
|
}
|
|
|
|
apModify.certstate = CERTIFICATE_OK;
|
|
cPrints += 1;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
apModify.certstate = CERTIFICATE_NOT_PRESENT;
|
|
}
|
|
|
|
SideAssert(SUCCEEDED(pAdrTbl->SetProps(apEntry.hAddress, &apModify)));
|
|
g_pMoleAlloc->FreeAddressProps(&apEntry);
|
|
}
|
|
|
|
// First off, are we sending to ourselves? If so, find our cert, make sure
|
|
// there's a cert associated with the account and then add it to the address
|
|
// table.
|
|
if ((pccertSender != NULL) && CheckAndFixMissingMeCert(hwnd, pAdrTbl, pAccount))
|
|
{
|
|
rgpccert[cPrints] = CertDuplicateCertificateContext(pccertSender);
|
|
cPrints += 1;
|
|
|
|
if (cCerts != cPrints)
|
|
goto NoCert;
|
|
}
|
|
|
|
else if (cCerts != cPrints) {
|
|
NoCert:
|
|
// Didn't get them all, tell the user.
|
|
CertErrParam.pAdrTable = pAdrTbl;
|
|
CertErrParam.fForceEncryption = FALSE;
|
|
if (pHeaderSite != NULL)
|
|
{
|
|
if(pHeaderSite->IsForceEncryption() == S_OK)
|
|
CertErrParam.fForceEncryption = TRUE;
|
|
}
|
|
|
|
if(IDOK ==DialogBoxParam(g_hLocRes, MAKEINTRESOURCE(iddSecCerificateErr),
|
|
hwnd, CertErrorDlgProc, (LPARAM)(&CertErrParam)))
|
|
hr = S_FALSE;
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
goto Exit;
|
|
}
|
|
else if(pccertSender != NULL) // include sender if we send encrypted message
|
|
{
|
|
rgpccert[cPrints] = CertDuplicateCertificateContext(pccertSender);
|
|
cPrints += 1;
|
|
}
|
|
|
|
*prgpccert = rgpccert;
|
|
*pcCert = cPrints;
|
|
|
|
Exit:
|
|
if (FAILED(hr)) {
|
|
for (i=0; i<cPrints; i++) {
|
|
if (rgpccert[i] != NULL) {
|
|
CertFreeCertificateContext(rgpccert[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
CertCloseStore(rgCertStore[1], 0);
|
|
CertCloseStore(rgCertStore[0], 0);
|
|
|
|
if(pAdrTbl)
|
|
ReleaseObj(pAdrTbl);
|
|
ReleaseObj(pAdrEnum);
|
|
return hr;
|
|
}
|
|
|
|
// This function parse error for signing certificate and do autoassociation in case if we have more signing certificate
|
|
//
|
|
HRESULT ProceedSignCertError(HWND hwnd, IImnAccount *pCertAccount, DWORD dwTrust)
|
|
{
|
|
ERRIDS ErrIds = {0, 0};
|
|
int cCert = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
if(!dwTrust)
|
|
return(S_OK);
|
|
|
|
// we must here parse the error
|
|
|
|
// Sure, the user might get multiple errors from this,
|
|
// but it is so rare I'll let this slide.
|
|
if (CERT_VALIDITY_BEFORE_START & dwTrust ||
|
|
CERT_VALIDITY_AFTER_END & dwTrust)
|
|
{
|
|
// do the fatal one first
|
|
ErrIds.idsText1 = idsErrSecuritySendExpiredSign;
|
|
}
|
|
else if(CERT_VALIDITY_OTHER_EXTENSION_FAILURE & dwTrust)
|
|
{
|
|
ErrIds.idsText1 = idsErrSecurityExtFailure;
|
|
}
|
|
else if(CERT_VALIDITY_CERTIFICATE_REVOKED & dwTrust)
|
|
{
|
|
ErrIds.idsText1 = idsErrSecurityCertRevoked;
|
|
}
|
|
else
|
|
{
|
|
ErrIds.idsText1 = idsErrSecuritySendTrust;
|
|
}
|
|
|
|
// Check # of available signed certificates for autoassociation.
|
|
cCert = GetNumMyCertForAccount(hwnd, pCertAccount, FALSE, NULL, NULL);
|
|
if(cCert < 1)
|
|
ErrIds.idsText2 = idsErrSignCertText20;
|
|
|
|
else if(cCert == 1)
|
|
ErrIds.idsText2 = idsErrSignCertText21;
|
|
|
|
else
|
|
ErrIds.idsText2 = idsErrSignCertText22;
|
|
|
|
INT uiRes = (INT) DialogBoxParam(g_hLocRes, MAKEINTRESOURCE(iddWarnSecuritySigningCert),
|
|
hwnd, CertWarnDlgProc, ((LPARAM)(&ErrIds)));
|
|
|
|
if(uiRes == IDCANCEL) // cancel, return to note
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
|
|
else if(uiRes == IDOK)
|
|
{
|
|
if(cCert < 1) // Go to web and return to note
|
|
{
|
|
GetDigitalIDs(pCertAccount);
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
}
|
|
else // Do autoassociation
|
|
hr = HR_E_ATHSEC_USENEWSIGN;
|
|
}
|
|
else // Don't sign
|
|
hr = HR_E_ATHSEC_DONTSIGN;
|
|
|
|
return(hr);
|
|
}
|
|
|
|
// This function parse error for encryption certificate and uf user like to do it then remove it from account
|
|
//
|
|
HRESULT ProceedEncCertError(HWND hwnd, IImnAccount *pCertAccount, DWORD dwTrust)
|
|
{
|
|
WORD ids = 0;
|
|
HRESULT hr = S_OK;
|
|
|
|
if(!dwTrust)
|
|
return(S_OK);
|
|
|
|
// we must here parse the error
|
|
|
|
// Sure, the user might get multiple errors from this,
|
|
// but it is so rare I'll let this slide.
|
|
if (CERT_VALIDITY_BEFORE_START & dwTrust ||
|
|
CERT_VALIDITY_AFTER_END & dwTrust)
|
|
{
|
|
// do the fatal one first
|
|
ids = idsErrSecuritySendExpSignEnc;
|
|
}
|
|
else if(CERT_VALIDITY_OTHER_EXTENSION_FAILURE & dwTrust)
|
|
{
|
|
ids = idsErrSecurityExtFailureEnc;
|
|
}
|
|
else if(CERT_VALIDITY_CERTIFICATE_REVOKED & dwTrust)
|
|
{
|
|
ids = idsErrSecurityCertRevokedEnc;
|
|
}
|
|
else
|
|
{
|
|
ids = idsErrSecuritySendTrustEnc;
|
|
}
|
|
|
|
if(AthMessageBoxW(hwnd,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(ids), MAKEINTRESOURCEW(idsErrEncCertCommon),
|
|
MB_YESNO| MB_ICONWARNING| MB_SETFOREGROUND) != IDYES)
|
|
{
|
|
// Cancel send message in this case
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
}
|
|
else
|
|
{
|
|
// remove wrong certificate from property
|
|
pCertAccount->SetProp(AP_SMTP_ENCRYPT_CERT, NULL, 0);
|
|
|
|
// send message anyway
|
|
hr = HR_E_ATHSEC_SAMEASSIGNED;
|
|
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
// Get certificate from Account, check it, display erro, set cheain to msg cert store
|
|
HRESULT HrPrepSignCert(HWND hwnd, BOOL fEncryptCert, BOOL fIsSigned, IImnAccount *pCertAccount, IMimeBody *pBody, PCX509CERT * ppCert,
|
|
BOOL *fDontEncryptForSelf, BOOL *fEncryptForMe, PCX509CERT pCertSig)
|
|
{
|
|
// need to check also encryption certificate and send it
|
|
THUMBBLOB tbCert = {0, 0};
|
|
HRESULT hr = S_OK;
|
|
HCERTSTORE hMyCertStore = NULL;
|
|
PROPVARIANT var;
|
|
DWORD dw = 0;
|
|
|
|
if (SUCCEEDED(hr = pCertAccount->GetProp((fEncryptCert ? AP_SMTP_ENCRYPT_CERT : AP_SMTP_CERTIFICATE), NULL, &tbCert.cbSize)))
|
|
{
|
|
// we have encryption certificate
|
|
hr = HrAlloc((void**)&tbCert.pBlobData, tbCert.cbSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pCertAccount->GetProp((fEncryptCert ? AP_SMTP_ENCRYPT_CERT : AP_SMTP_CERTIFICATE), tbCert.pBlobData, &tbCert.cbSize);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hMyCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING,
|
|
NULL, CERT_SYSTEM_STORE_CURRENT_USER, c_szMyCertStore);
|
|
if (hMyCertStore)
|
|
{
|
|
//
|
|
// Check the chain
|
|
//
|
|
X509CERTRESULT certResult;
|
|
CERTSTATE cs;
|
|
PCX509CERT pCert = NULL;
|
|
|
|
certResult.cEntries = 1;
|
|
certResult.rgcs = &cs;
|
|
certResult.rgpCert = &pCert;
|
|
|
|
hr = MimeOleGetCertsFromThumbprints(&tbCert, &certResult, &hMyCertStore, 1);
|
|
if (S_OK == hr)
|
|
{
|
|
DWORD dwTrust;
|
|
PCCERT_CONTEXT *rgCertChain = NULL;
|
|
DWORD cCertChain = 0;
|
|
const DWORD dwIgnore = CERT_VALIDITY_NO_CRL_FOUND;
|
|
|
|
Assert(1 == certResult.cEntries);
|
|
|
|
// If we asking about encryption certificate we need to check that it's not the same
|
|
// as signrd
|
|
if(fEncryptCert && fIsSigned)
|
|
{
|
|
if(CertCompareCertificate(X509_ASN_ENCODING, pCert->pCertInfo, pCertSig->pCertInfo))
|
|
{
|
|
hr = HR_E_ATHSEC_SAMEASSIGNED;
|
|
goto Exit;
|
|
}
|
|
}
|
|
// As to CRLs, if we'll ever have one!
|
|
dwTrust = DwGenerateTrustedChain(hwnd, NULL, pCert,
|
|
dwIgnore, TRUE, &cCertChain, &rgCertChain);
|
|
|
|
if (dwTrust)
|
|
{
|
|
if (fIsSigned)
|
|
{
|
|
if(fEncryptCert)
|
|
{
|
|
if(pCertSig && SUCCEEDED(HrGetCertKeyUsage(pCertSig, &dw)))
|
|
{
|
|
if ((dw & (CERT_KEY_ENCIPHERMENT_KEY_USAGE |
|
|
CERT_KEY_AGREEMENT_KEY_USAGE)))
|
|
{
|
|
hr = HR_E_ATHSEC_SAMEASSIGNED;
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HR_E_ATHSEC_SAMEASSIGNED;
|
|
goto Exit;
|
|
}
|
|
|
|
hr = ProceedEncCertError(hwnd, pCertAccount, dwTrust);
|
|
}
|
|
else
|
|
hr = ProceedSignCertError(hwnd, pCertAccount, dwTrust);
|
|
|
|
}
|
|
else
|
|
{ // Encryption certificate
|
|
// we must here parse the error
|
|
WORD ids;
|
|
BOOL fFatal = TRUE;
|
|
|
|
// Sure, the user might get multiple errors from this,
|
|
// but it is so rare I'll let this slide.
|
|
if (CERT_VALIDITY_BEFORE_START & dwTrust ||
|
|
CERT_VALIDITY_AFTER_END & dwTrust)
|
|
{
|
|
// Assert(IsEncrypted(pMsg, TRUE));
|
|
ids = idsErrSecuritySendExpiredEnc;
|
|
fFatal = FALSE;
|
|
}
|
|
else if(CERT_VALIDITY_OTHER_EXTENSION_FAILURE & dwTrust)
|
|
{
|
|
ids = idsErrSecurityExtFailure;
|
|
fFatal = TRUE;
|
|
}
|
|
else if(CERT_VALIDITY_CERTIFICATE_REVOKED & dwTrust)
|
|
{
|
|
ids = (fEncryptCert ? idsErrSecurityCertRevokedEnc : idsErrSecurityCertRevoked);
|
|
fFatal = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ids = idsErrSecuritySendTrustEnc;
|
|
fFatal = FALSE;
|
|
}
|
|
if (fFatal)
|
|
{
|
|
AthMessageBoxW(hwnd,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(ids), NULL,
|
|
MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
}
|
|
else
|
|
{
|
|
if (IDYES != AthMessageBoxW(hwnd,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(ids), MAKEINTRESOURCEW(idsWrnSecEncryption),
|
|
MB_YESNO|MB_ICONWARNING|MB_SETFOREGROUND))
|
|
{
|
|
// This error code has the special meaning of
|
|
// "don't show any more UI, the user has been
|
|
// beaten enough"
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
}
|
|
else
|
|
{
|
|
if(!fEncryptCert)
|
|
{
|
|
// Since the user's cert is expired, we won't
|
|
// let it be used to encrypt
|
|
*fEncryptForMe = FALSE;
|
|
*fDontEncryptForSelf = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else // no errors
|
|
{
|
|
// We need to add encryption certificate to message cert store
|
|
#ifdef _WIN64 // (YST) I believe Win64 part will be not work...
|
|
if (DwGetOption(OPT_MAIL_INCLUDECERT))
|
|
{
|
|
var.vt = VT_VECTOR | VT_UI8;
|
|
if (0 != cCertChain)
|
|
{
|
|
var.cauh.cElems = cCertChain;
|
|
var.cauh.pElems = (ULARGE_INTEGER *)rgCertChain;
|
|
}
|
|
else
|
|
{
|
|
var.cauh.cElems = 1;
|
|
var.cauh.pElems = (ULARGE_INTEGER *)&pCert;
|
|
}
|
|
hr = pBody->SetOption(OID_SECURITY_RG_CERT_BAG_64, &var);
|
|
Assert((cCertChain > 0) || dwTrust);
|
|
}
|
|
#else //_WIN64
|
|
if (DwGetOption(OPT_MAIL_INCLUDECERT))
|
|
{
|
|
var.vt = VT_VECTOR | VT_UI4;
|
|
if (0 != cCertChain)
|
|
{
|
|
var.caul.cElems = cCertChain;
|
|
var.caul.pElems = (ULONG *)rgCertChain;
|
|
}
|
|
else
|
|
{
|
|
var.caul.cElems = 1;
|
|
var.caul.pElems = (ULONG *)&pCert;
|
|
}
|
|
hr = pBody->SetOption(OID_SECURITY_2KEY_CERT_BAG, &var);
|
|
Assert((cCertChain > 0) || dwTrust);
|
|
}
|
|
#endif // _WIN64
|
|
}
|
|
Exit:
|
|
*ppCert = pCert;
|
|
// Still might have a chain on error, so run the
|
|
// freeing code outside the result test
|
|
|
|
if (rgCertChain)
|
|
{
|
|
for (cCertChain--; int(cCertChain)>=0; cCertChain--)
|
|
CertFreeCertificateContext(rgCertChain[cCertChain]);
|
|
MemFree(rgCertChain);
|
|
}
|
|
}
|
|
else
|
|
hr = MIME_S_SECURITY_ERROROCCURED;
|
|
}
|
|
else
|
|
hr = MIME_S_SECURITY_ERROROCCURED;
|
|
|
|
if(hMyCertStore)
|
|
CertCloseStore(hMyCertStore, 0);
|
|
}
|
|
else
|
|
hr = MIME_S_SECURITY_ERROROCCURED;
|
|
}
|
|
if(tbCert.pBlobData)
|
|
MemFree(tbCert.pBlobData);
|
|
}
|
|
else
|
|
{
|
|
if (fIsSigned)
|
|
{
|
|
if(fEncryptCert)
|
|
{
|
|
if(pCertSig && SUCCEEDED(HrGetCertKeyUsage(pCertSig, &dw)))
|
|
{
|
|
if ((dw & (CERT_KEY_ENCIPHERMENT_KEY_USAGE |
|
|
CERT_KEY_AGREEMENT_KEY_USAGE)))
|
|
{
|
|
hr = HR_E_ATHSEC_SAMEASSIGNED;
|
|
return(hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HR_E_ATHSEC_SAMEASSIGNED;
|
|
return(hr);
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = MIME_S_SECURITY_ERROROCCURED;
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT _HrPrepSecureMsgForSending(HWND hwnd, LPMIMEMESSAGE pMsg, IImnAccount *pAccount, BOOL *pfHaveSenderCert, BOOL *fDontEncryptForSelf, IHeaderSite *pHeaderSite)
|
|
{
|
|
HRESULT hr;
|
|
IMimeBody *pBody = NULL;
|
|
// THUMBBLOB tbSender = {0, 0};
|
|
BOOL fIsSigned = IsSigned(pMsg, TRUE);
|
|
BOOL fIsEncrypted = IsEncrypted(pMsg, TRUE);
|
|
BOOL fAllowTryAgain = TRUE;
|
|
IImnAccount *pCertAccount = NULL;
|
|
ACCTTYPE acctType;
|
|
PROPVARIANT var;
|
|
SYSTEMTIME stNow;
|
|
DWORD cCert=0;
|
|
DWORD i;
|
|
PCCERT_CONTEXT pccertSender = NULL;
|
|
PCX509CERT * rgpccert = NULL;
|
|
|
|
hr = pAccount->GetAccountType(&acctType);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
if (ACCT_NEWS == acctType)
|
|
{
|
|
if (IDCANCEL == DoDontShowMeAgainDlg(hwnd, c_szDSUseMailCertInNews, MAKEINTRESOURCE(idsAthena),
|
|
MAKEINTRESOURCE(idsWarnUseMailCertInNews), MB_OKCANCEL))
|
|
hr = MAPI_E_USER_CANCEL;
|
|
else
|
|
hr = g_pAcctMan->GetDefaultAccount(ACCT_MAIL, &pCertAccount);
|
|
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
}
|
|
else
|
|
ReplaceInterface(pCertAccount, pAccount);
|
|
|
|
Assert(pMsg && pAccount && pfHaveSenderCert);
|
|
|
|
*pfHaveSenderCert = FALSE;
|
|
|
|
hr = pMsg->BindToObject(HBODY_ROOT, IID_IMimeBody, (void **)&pBody);
|
|
if (FAILED(hr))
|
|
goto Exit;
|
|
|
|
GetSystemTime(&stNow);
|
|
SystemTimeToFileTime(&stNow, &var.filetime);
|
|
var.vt = VT_FILETIME;
|
|
pBody->SetOption(OID_SECURITY_SIGNTIME, &var);
|
|
|
|
// Here, we should check the encryption algorithm
|
|
if (fIsEncrypted)
|
|
{
|
|
if (SUCCEEDED(hr = pBody->GetOption(OID_SECURITY_ALG_BULK, &var)))
|
|
{
|
|
DWORD dwStrength = 40; // default
|
|
DWORD dwWarnStrength = DwGetOption(OPT_MAIL_ENCRYPT_WARN_BITS);
|
|
|
|
// Figure out the bit-strength of this algorithm.
|
|
Assert(var.vt == VT_BLOB);
|
|
|
|
if (var.blob.pBlobData)
|
|
{
|
|
MimeOleAlgStrengthFromSMimeCap(var.blob.pBlobData, var.blob.cbSize, TRUE, &dwStrength);
|
|
SafeMemFree(var.blob.pBlobData);
|
|
}
|
|
|
|
if (! dwWarnStrength) // zero is the default to highest available
|
|
// Calculate the highest available
|
|
dwWarnStrength = GetHighestEncryptionStrength();
|
|
|
|
if (dwStrength < dwWarnStrength)
|
|
{
|
|
// Load the warning string and fill it in with the numbers.
|
|
LPTSTR lpMessage = NULL;
|
|
DWORD rgdw[2] = {dwStrength, dwWarnStrength};
|
|
TCHAR szBuffer[256] = ""; // really ought to be big enough
|
|
DWORD dwResult = IDNO;
|
|
|
|
LoadString(g_hLocRes, idsWrnLowSecurity, szBuffer, sizeof(szBuffer));
|
|
|
|
if (szBuffer[0])
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_STRING |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY, szBuffer,
|
|
0, 0,
|
|
(LPTSTR)&lpMessage, 0, (va_list *)rgdw);
|
|
|
|
if (lpMessage)
|
|
{
|
|
dwResult = AthMessageBox(hwnd,
|
|
MAKEINTRESOURCE(idsSecurityWarning),
|
|
lpMessage,
|
|
NULL,
|
|
MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING | MB_SETFOREGROUND);
|
|
|
|
LocalFree(lpMessage); // allocated by WIN32 inside FormatMessage
|
|
}
|
|
|
|
if (IDYES != dwResult)
|
|
{
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
try_again:
|
|
if ((*fDontEncryptForSelf))
|
|
hr = E_NoPropData;
|
|
if ((!(*fDontEncryptForSelf) || fIsSigned))
|
|
{
|
|
PCX509CERT pCert = NULL;
|
|
BOOL fEncryptForMe = TRUE;
|
|
|
|
#ifndef _WIN64
|
|
var.ulVal = 0;
|
|
var.vt = VT_UI4;
|
|
hr = pBody->SetOption(OID_SECURITY_HCERTSTORE, &var);
|
|
#else
|
|
var.pulVal = 0;
|
|
var.vt = VT_UI8;
|
|
hr = pBody->SetOption(OID_SECURITY_HCERTSTORE_64, &var);
|
|
#endif
|
|
|
|
hr = S_OK;
|
|
if(fIsSigned)
|
|
hr = HrPrepSignCert(hwnd,
|
|
FALSE /* fEncryptCert*/,
|
|
fIsSigned,
|
|
pCertAccount,
|
|
pBody,
|
|
&pCert,
|
|
fDontEncryptForSelf,
|
|
&fEncryptForMe,
|
|
NULL);
|
|
|
|
else
|
|
pCert = NULL;
|
|
|
|
// This will only be valid if we got an S_OK from the cert
|
|
// finder.
|
|
if(hr == S_OK)
|
|
{
|
|
#ifdef _WIN64
|
|
var.vt = VT_UI8;
|
|
var.pulVal = (ULONG *) pCert;
|
|
hr = pBody->SetOption(OID_SECURITY_CERT_SIGNING_64, &var);
|
|
#else //_WIN64
|
|
var.vt = VT_UI4;
|
|
var.ulVal = (ULONG) pCert;
|
|
if(fIsSigned)
|
|
hr = pBody->SetOption(OID_SECURITY_CERT_SIGNING, &var);
|
|
#endif // _WIN64
|
|
|
|
// need to check also encryption certificate and send it
|
|
PCX509CERT pCertEnc = NULL;
|
|
fAllowTryAgain = TRUE;
|
|
|
|
try_encrypt:
|
|
hr = HrPrepSignCert(hwnd,
|
|
TRUE, // fEncryptCert,
|
|
fIsSigned,
|
|
pCertAccount,
|
|
pBody,
|
|
&pCertEnc,
|
|
fDontEncryptForSelf,
|
|
&fEncryptForMe,
|
|
pCert // Signed certificate
|
|
);
|
|
|
|
if((hr == HR_E_ATHSEC_SAMEASSIGNED) || // Encryption certificate is the same as signed
|
|
(hr == E_NoPropData))
|
|
{
|
|
if(fEncryptForMe)
|
|
{
|
|
pccertSender = CertDuplicateCertificateContext(pCert);
|
|
*pfHaveSenderCert = TRUE;
|
|
}
|
|
// Do nothing in this case
|
|
hr = S_OK;
|
|
goto EncrDone;
|
|
}
|
|
|
|
// This will only be valid if we got an S_OK from the cert
|
|
// finder. This is because we only ask for one.
|
|
if(hr == S_OK)
|
|
{
|
|
|
|
if(fEncryptForMe)
|
|
{
|
|
pccertSender = CertDuplicateCertificateContext(pCertEnc);
|
|
*pfHaveSenderCert = TRUE;
|
|
}
|
|
|
|
if(!fIsSigned)
|
|
goto EncrDone;
|
|
|
|
// Now wee need to set auth attribute
|
|
// OE will use Issuer and Serial # for searching certificate in Message store
|
|
// 1. Prepare CRYPT_RECIPIENT_ID
|
|
// 2. Add it to the message
|
|
|
|
CRYPT_RECIPIENT_ID rid;
|
|
LPBYTE pbData = NULL;
|
|
DWORD cbData = 0;
|
|
CRYPT_ATTRIBUTE attr;
|
|
CRYPT_ATTRIBUTES Attrs;
|
|
CRYPT_ATTR_BLOB BlobEnc;
|
|
LPBYTE pbBlob = NULL;
|
|
DWORD cbBlob = 0;
|
|
IMimeSecurity2 * psm2 = NULL;
|
|
|
|
IF_FAILEXIT(hr = pBody->QueryInterface(IID_IMimeSecurity2, (LPVOID *) &psm2));
|
|
|
|
// 1. Prepare CRYPT_RECIPIENT_ID
|
|
|
|
rid.dwRecipientType = 0;
|
|
rid.Issuer = pCertEnc->pCertInfo->Issuer;
|
|
rid.SerialNumber = pCertEnc->pCertInfo->SerialNumber;
|
|
|
|
BOOL fResult = CryptEncodeObjectEx(X509_ASN_ENCODING, szOID_Microsoft_Encryption_Cert,
|
|
&rid, CRYPT_ENCODE_ALLOC_FLAG, &CryptEncodeAlloc,
|
|
&pbData,
|
|
&cbData);
|
|
|
|
// Fatal error if fResult is FALSE,
|
|
// need a parsing
|
|
if(!fResult)
|
|
{
|
|
FatalEnc:
|
|
CertFreeCertificateContext(pCertEnc);
|
|
AthMessageBoxW(hwnd,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(idsErrSecurityExtFailure), NULL,
|
|
MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
goto exit;
|
|
}
|
|
|
|
// 2. Prepare array of attributes (in our case just 1)
|
|
BlobEnc.cbData = cbData;
|
|
BlobEnc.pbData = pbData;
|
|
|
|
attr.pszObjId = szOID_Microsoft_Encryption_Cert;
|
|
attr.cValue = 1;
|
|
attr.rgValue = &BlobEnc;
|
|
|
|
hr = psm2->SetAttribute(0, 0, SMIME_ATTRIBUTE_SET_SIGNED, &attr);
|
|
if (FAILED(hr))
|
|
goto FatalEnc;
|
|
|
|
psm2->Release();
|
|
}
|
|
else if (MIME_S_SECURITY_ERROROCCURED == hr)
|
|
{
|
|
// We are missing a encryption cert. If it is signed or encrypted
|
|
// go try to find a cert to use.
|
|
//
|
|
// If it is encrypted to someone else, let the S/MIME engine handle
|
|
// This allows the order of errors to become
|
|
// 1) errors for lack of recip certs
|
|
// 2) warn no sender cert
|
|
if (fAllowTryAgain && (fIsSigned || !(*fDontEncryptForSelf)))
|
|
{
|
|
fAllowTryAgain = FALSE; // Only one more try, please. Prevent infinite loop.
|
|
|
|
// Is there a cert that I can use? If so, let's go associate it with the
|
|
// account and try again.
|
|
if (SUCCEEDED(hr = _HrFindMyCertForAccount(hwnd, NULL, pAccount, TRUE)))
|
|
{
|
|
// Go back and try again.
|
|
pCertEnc = NULL;
|
|
goto try_encrypt;
|
|
}
|
|
else if(hr != MAPI_E_USER_CANCEL)
|
|
{
|
|
if(!fIsEncrypted || (IDYES == AthMessageBoxW(hwnd,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(idsWrnSecurityNoCertForEnc), NULL,
|
|
MB_YESNO|MB_DEFBUTTON2|MB_ICONWARNING|MB_SETFOREGROUND)))
|
|
hr = S_OK;
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
EncrDone:
|
|
if(pCertEnc)
|
|
CertFreeCertificateContext(pCertEnc);
|
|
|
|
if((HR_E_ATHSEC_FAILED != hr) && (hr != MAPI_E_USER_CANCEL))
|
|
{
|
|
|
|
// check secutity label
|
|
if ((pHeaderSite != NULL) && fIsSigned)
|
|
{
|
|
PSMIME_SECURITY_LABEL plabel = NULL;
|
|
CRYPT_ATTRIBUTE attrCrypt;
|
|
CRYPT_ATTR_BLOB valCrypt;
|
|
IMimeSecurity2 * pSMIME3 = NULL;
|
|
|
|
if(pBody->QueryInterface(IID_IMimeSecurity2, (LPVOID *) &pSMIME3) == S_OK)
|
|
{
|
|
|
|
hr = pHeaderSite->GetLabelFromNote(&plabel);
|
|
|
|
if(hr == S_OK )
|
|
{
|
|
if((plabel != NULL) && SUCCEEDED(hr = HrValidateLabelOnSend(plabel, hwnd, pCert, 0, NULL)))
|
|
{
|
|
LPBYTE pbLabel = NULL;
|
|
DWORD cbLabel;
|
|
|
|
// hr = HrEncodeAndAlloc(X509_ASN_ENCODING,
|
|
// szOID_SMIME_Security_Label, plabel,
|
|
// &pbLabel, &cbLabel);
|
|
|
|
if(CryptEncodeObjectEx(X509_ASN_ENCODING, szOID_SMIME_Security_Label,
|
|
plabel, CRYPT_ENCODE_ALLOC_FLAG, &CryptEncodeAlloc,
|
|
&pbLabel, &cbLabel))
|
|
{
|
|
attrCrypt.pszObjId = szOID_SMIME_Security_Label;
|
|
attrCrypt.cValue = 1;
|
|
attrCrypt.rgValue = &valCrypt;
|
|
valCrypt.cbData = cbLabel;
|
|
valCrypt.pbData = pbLabel;
|
|
hr = pSMIME3->SetAttribute(0, 0, SMIME_ATTRIBUTE_SET_SIGNED, &attrCrypt);
|
|
}
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
|
|
SafeMemFree(pbLabel);
|
|
}
|
|
else
|
|
{
|
|
if(SUCCEEDED(hr))
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
if(hr != HR_E_ATHSEC_FAILED)
|
|
{
|
|
AthMessageBoxW(hwnd,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(idsSecPolicyBadCert), NULL,
|
|
MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
}
|
|
else if(IDYES == AthMessageBoxW(hwnd,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(idsSendLabelErr), NULL,
|
|
MB_YESNO|MB_DEFBUTTON2|MB_ICONWARNING|MB_SETFOREGROUND))
|
|
hr = S_OK;
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
|
|
}
|
|
if(FAILED(hr))
|
|
{
|
|
SafeRelease(pSMIME3);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// Check security receipts
|
|
if (pHeaderSite->IsSecReceiptRequest() == S_OK)
|
|
{
|
|
CERT_NAME_BLOB blob;
|
|
// DATA_BLOB BlobId;
|
|
|
|
SMIME_RECEIPT_REQUEST req = {0};
|
|
DWORD cbName;
|
|
SpBYTE pbName;
|
|
LPBYTE pbReq = NULL;
|
|
DWORD cbReq = 0;
|
|
|
|
CERT_ALT_NAME_INFO SenderName;
|
|
PCERT_ALT_NAME_ENTRY palt;
|
|
|
|
// Get a sender name and encrypt it
|
|
LPSTR szCertEmailAddress = SzGetCertificateEmailAddress(pCert);
|
|
Assert(szCertEmailAddress != NULL);
|
|
|
|
if(MemAlloc((LPVOID *) &palt, sizeof(CERT_ALT_NAME_ENTRY)))
|
|
{
|
|
TCHAR pchContentID[CONTENTID_SIZE]; // length this will be 46. See CreateContentIdentifier comment
|
|
|
|
CreateContentIdentifier(pchContentID, ARRAYSIZE(pchContentID), pMsg);
|
|
|
|
MimeOleSetBodyPropA(pMsg, HBODY_ROOT, STR_HDR_XMSOESREC, NOFLAGS, pchContentID);
|
|
req.ContentIdentifier.pbData = (BYTE *) pchContentID;
|
|
req.ContentIdentifier.cbData = lstrlen(pchContentID);
|
|
|
|
palt->pwszRfc822Name = NULL;
|
|
palt->dwAltNameChoice = CERT_ALT_NAME_RFC822_NAME;
|
|
|
|
cbName = MultiByteToWideChar(CP_ACP, 0, szCertEmailAddress, -1, NULL, 0);
|
|
|
|
if (MemAlloc((LPVOID *) &(palt->pwszRfc822Name), (cbName + 1)*sizeof(WCHAR)))
|
|
{
|
|
|
|
MultiByteToWideChar(CP_ACP, 0, szCertEmailAddress, -1, palt->pwszRfc822Name, cbName);
|
|
SenderName.cAltEntry = 1;
|
|
SenderName.rgAltEntry = palt;
|
|
|
|
if(CryptEncodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
|
|
&SenderName, CRYPT_ENCODE_ALLOC_FLAG, &CryptEncodeAlloc,
|
|
&pbName, &cbName))
|
|
{
|
|
|
|
blob.pbData = pbName;
|
|
blob.cbData = cbName;
|
|
|
|
// create sec receipt request
|
|
req.ReceiptsFrom.AllOrFirstTier = SMIME_RECEIPTS_FROM_ALL;
|
|
req.ReceiptsFrom.cNames = 0;
|
|
|
|
req.cReceiptsTo = 1;
|
|
req.rgReceiptsTo = &blob;
|
|
|
|
// Encrypt it
|
|
if(CryptEncodeObjectEx(X509_ASN_ENCODING, szOID_SMIME_Receipt_Request,
|
|
&req, CRYPT_ENCODE_ALLOC_FLAG, &CryptEncodeAlloc,
|
|
&pbReq, &cbReq))
|
|
{
|
|
|
|
// Set sec receipt request attribute
|
|
attrCrypt.pszObjId = szOID_SMIME_Receipt_Request;
|
|
attrCrypt.cValue = 1;
|
|
attrCrypt.rgValue = &valCrypt;
|
|
valCrypt.cbData = cbReq;
|
|
valCrypt.pbData = pbReq;
|
|
|
|
hr = pSMIME3->SetAttribute(0, 0, SMIME_ATTRIBUTE_SET_SIGNED, &attrCrypt);
|
|
|
|
Assert(hr == S_OK);
|
|
SafeMemFree(pbReq);
|
|
}
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
|
|
SafeMemFree(pbName);
|
|
}
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
|
|
SafeMemFree(palt->pwszRfc822Name);
|
|
}
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
|
|
SafeMemFree(palt);
|
|
}
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
if(IDYES == AthMessageBoxW(hwnd,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(idsSendRecRequestErr), NULL,
|
|
MB_YESNO|MB_DEFBUTTON2|MB_ICONWARNING|MB_SETFOREGROUND))
|
|
hr = S_OK;
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
|
|
}
|
|
if(FAILED(hr))
|
|
goto exit;
|
|
SafeMemFree(szCertEmailAddress);
|
|
|
|
}
|
|
SafeRelease(pSMIME3);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
else if (MIME_S_SECURITY_ERROROCCURED == hr)
|
|
{
|
|
// We are missing a MY cert. If it is signed or encrypted
|
|
// go try to find a cert to use.
|
|
//
|
|
// If it is encrypted to someone else, let the S/MIME engine handle
|
|
// This allows the order of errors to become
|
|
// 1) errors for lack of recip certs
|
|
// 2) warn no sender cert
|
|
hr = fIsSigned ? HR_E_ATHSEC_NOCERTTOSIGN : S_OK;
|
|
|
|
if (fAllowTryAgain && (fIsSigned || !(*fDontEncryptForSelf)))
|
|
{
|
|
fAllowTryAgain = FALSE; // Only one more try, please. Prevent infinite loop.
|
|
|
|
// Is there a cert that I can use? If so, let's go associate it with the
|
|
// account and try again.
|
|
if (SUCCEEDED(hr = _HrFindMyCertForAccount(hwnd, NULL, pAccount, /*fIsSigned ?*/ FALSE /*: TRUE*/)))
|
|
// Go back and try again.
|
|
goto try_again;
|
|
else if(fIsEncrypted)
|
|
{
|
|
if(IDYES == AthMessageBoxW(hwnd,
|
|
MAKEINTRESOURCEW(idsSecurityWarning),
|
|
MAKEINTRESOURCEW(idsWrnSecurityNoCertForEnc), NULL,
|
|
MB_YESNO|MB_DEFBUTTON2|MB_ICONWARNING|MB_SETFOREGROUND))
|
|
hr = S_OK;
|
|
else
|
|
hr = HR_E_ATHSEC_FAILED;
|
|
}
|
|
|
|
}
|
|
}
|
|
// This will only be valid if we got an S_OK from the cert
|
|
// finder. This is because we only ask for one.
|
|
|
|
if(pCert)
|
|
CertFreeCertificateContext(pCert);
|
|
}
|
|
else
|
|
{
|
|
if (E_NoPropData == hr)
|
|
{
|
|
BOOL fTryAgain = FALSE;
|
|
DOUTL(DOUTL_CRYPT, "No certificate for this account...");
|
|
|
|
// We are missing a MY cert. If it is signed or encrypted
|
|
// go try to find a cert to use.
|
|
//
|
|
// If it is encrypted to someone else, let the S/MIME engine handle
|
|
// This allows the order of errors to become
|
|
// 1) errors for lack of recip certs
|
|
// 2) warn no sender cert
|
|
hr = fIsSigned ? HR_E_ATHSEC_NOCERTTOSIGN : S_OK;
|
|
|
|
if (fAllowTryAgain && (fIsSigned || !(*fDontEncryptForSelf)))
|
|
{
|
|
fAllowTryAgain = FALSE; // Only one more try, please. Prevent infinite loop.
|
|
|
|
// Is there a cert that I can use? If so, let's go associate it with the
|
|
// account and try again.
|
|
if (SUCCEEDED(hr = _HrFindMyCertForAccount(hwnd, NULL, pAccount, TRUE)))
|
|
// Go back and try again.
|
|
goto try_again;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
goto Exit;
|
|
|
|
if (fIsEncrypted)
|
|
{
|
|
if((hr = HrBuildAndVerifyCerts(hwnd, pMsg, &cCert, &rgpccert, pccertSender, pAccount, pHeaderSite)) == S_FALSE)
|
|
{
|
|
ULONG ulSecurityType = MST_CLASS_SMIME_V1;
|
|
|
|
if (fIsSigned)
|
|
ulSecurityType |= ((DwGetOption(OPT_OPAQUE_SIGN)) ? MST_THIS_BLOBSIGN : MST_THIS_SIGN);
|
|
else
|
|
ulSecurityType &= ~((DwGetOption(OPT_OPAQUE_SIGN)) ? MST_THIS_BLOBSIGN : MST_THIS_SIGN);
|
|
|
|
|
|
ulSecurityType &= ~MST_THIS_ENCRYPT;
|
|
hr = HrInitSecurityOptions(pMsg, ulSecurityType);
|
|
fIsEncrypted = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
CHECKHR(hr);
|
|
|
|
if (cCert > 0)
|
|
{
|
|
PROPVARIANT var;
|
|
|
|
#ifdef _WIN64
|
|
var.vt = VT_UI8;
|
|
var.cauh.cElems = cCert;
|
|
var.cauh.pElems = (ULARGE_INTEGER *) rgpccert;
|
|
CHECKHR(hr = pBody->SetOption(OID_SECURITY_RG_CERT_ENCRYPT_64, &var));
|
|
#else // !_WIN64
|
|
var.vt = VT_UI4;
|
|
var.caul.cElems = cCert;
|
|
var.caul.pElems = (ULONG *) rgpccert;
|
|
CHECKHR(hr = pBody->SetOption(OID_SECURITY_RG_CERT_ENCRYPT, &var));
|
|
#endif // _WIN64
|
|
}
|
|
}
|
|
Exit:
|
|
// For signing messages
|
|
if(fIsSigned)
|
|
{
|
|
// Do autoassociation
|
|
if(hr == HR_E_ATHSEC_USENEWSIGN)
|
|
{
|
|
if (SUCCEEDED(hr = _HrFindMyCertForAccount(hwnd, NULL, pAccount, FALSE)))
|
|
{
|
|
// Go back and try again.
|
|
fAllowTryAgain = TRUE;
|
|
goto try_again;
|
|
}
|
|
}
|
|
else if(hr == HR_E_ATHSEC_DONTSIGN) // Don't sign message
|
|
{
|
|
ULONG ulSecurityType = MST_CLASS_SMIME_V1;
|
|
|
|
if (fIsEncrypted)
|
|
ulSecurityType |= MST_THIS_ENCRYPT;
|
|
|
|
ulSecurityType &= ~((DwGetOption(OPT_OPAQUE_SIGN)) ? MST_THIS_BLOBSIGN : MST_THIS_SIGN);
|
|
|
|
hr = HrInitSecurityOptions(pMsg, ulSecurityType);
|
|
fIsSigned = FALSE;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
ReleaseObj(pBody);
|
|
ReleaseObj(pCertAccount);
|
|
if (rgpccert != NULL)
|
|
{
|
|
for (i=0; i<cCert; i++)
|
|
{
|
|
if (rgpccert[i] != NULL)
|
|
CertFreeCertificateContext(rgpccert[i]);
|
|
}
|
|
if (pccertSender != NULL)
|
|
CertFreeCertificateContext(pccertSender);
|
|
}
|
|
return TrapError(hr);
|
|
}
|
|
|
|
DWORD DwGenerateTrustedChain(
|
|
HWND hwnd,
|
|
IMimeMessage * pMsg,
|
|
PCCERT_CONTEXT pcCertToTest,
|
|
DWORD dwToIgnore,
|
|
BOOL fFullSearch,
|
|
DWORD * pcChain,
|
|
PCCERT_CONTEXT ** prgChain)
|
|
{
|
|
DWORD dwErr = 0;
|
|
GUID guidAction = CERT_CERTIFICATE_ACTION_VERIFY;
|
|
CERT_VERIFY_CERTIFICATE_TRUST trust = {0};
|
|
WINTRUST_BLOB_INFO blob = {0};
|
|
WINTRUST_DATA data = {0};
|
|
IMimeBody * pBody;
|
|
PROPVARIANT var;
|
|
HCERTSTORE rgCAs[3] = {0};
|
|
HCERTSTORE *pCAs = NULL;
|
|
HCERTSTORE hMsg = NULL;
|
|
FILETIME FileTime;
|
|
SYSTEMTIME SysTime;
|
|
LONG lr = 0;
|
|
BOOL fIgnoreTimeError = FALSE;
|
|
HBODY hBody = NULL;
|
|
|
|
|
|
Assert(pcCertToTest);
|
|
|
|
if (pMsg)
|
|
{
|
|
if(FAILED(HrGetInnerLayer(pMsg, &hBody)))
|
|
goto contin;
|
|
|
|
pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (void**)&pBody);
|
|
if (pBody)
|
|
{
|
|
#ifdef _WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE_64, &var)))
|
|
hMsg = (HCERTSTORE) (var.pulVal);
|
|
#else // !_WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE, &var)))
|
|
hMsg = (HCERTSTORE)var.ulVal;
|
|
#endif // _WIN64
|
|
pBody->Release();
|
|
|
|
if (hMsg)
|
|
{
|
|
rgCAs[0] = hMsg;
|
|
|
|
rgCAs[1] = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, 0, CERT_SYSTEM_STORE_CURRENT_USER, c_szMyCertStore);
|
|
if (rgCAs[1])
|
|
{
|
|
rgCAs[2] = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, 0, CERT_SYSTEM_STORE_CURRENT_USER, c_szCACertStore);
|
|
if (rgCAs[2])
|
|
pCAs = rgCAs;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
contin:
|
|
data.cbStruct = sizeof(WINTRUST_DATA);
|
|
data.pPolicyCallbackData = NULL;
|
|
data.pSIPClientData = NULL;
|
|
data.dwUIChoice = WTD_UI_NONE;
|
|
data.fdwRevocationChecks = WTD_REVOKE_NONE;
|
|
data.dwUnionChoice = WTD_CHOICE_BLOB;
|
|
data.pBlob = &blob;
|
|
|
|
blob.cbStruct = sizeof(WINTRUST_BLOB_INFO);
|
|
blob.pcwszDisplayName = NULL;
|
|
blob.cbMemObject = sizeof(trust);
|
|
blob.pbMemObject = (LPBYTE)&trust;
|
|
|
|
trust.cbSize = sizeof(trust);
|
|
trust.pccert = pcCertToTest;
|
|
trust.dwFlags = (fFullSearch ? CERT_TRUST_DO_FULL_SEARCH : 0);
|
|
trust.pdwErrors = &dwErr;
|
|
trust.pszUsageOid = szOID_PKIX_KP_EMAIL_PROTECTION;
|
|
trust.pcChain = pcChain;
|
|
trust.prgChain = prgChain;
|
|
|
|
if(!((DwGetOption(OPT_REVOKE_CHECK) != 0) && !g_pConMan->IsGlobalOffline() && CheckCDPinCert(pcCertToTest)))
|
|
trust.dwFlags |= CRYPTDLG_REVOCATION_NONE;
|
|
else
|
|
trust.dwFlags |= CRYPTDLG_REVOCATION_ONLINE;
|
|
|
|
//cvct.prgdwErrors
|
|
trust.dwIgnoreErr = dwToIgnore;
|
|
if (pCAs)
|
|
{
|
|
trust.dwFlags |= CERT_TRUST_ADD_CERT_STORES;
|
|
trust.rghstoreCAs = pCAs;
|
|
trust.cStores = 3;
|
|
}
|
|
|
|
// Delta checking
|
|
GetSystemTime(&SysTime);
|
|
if(SystemTimeToFileTime(&SysTime, &FileTime))
|
|
{
|
|
LONG lRet;
|
|
// Need to check with Delta
|
|
lr = CertVerifyTimeValidity(&FileTime, pcCertToTest->pCertInfo);
|
|
if(lr < 0)
|
|
{
|
|
FILETIME ftNow;
|
|
__int64 i64Offset;
|
|
|
|
union
|
|
{
|
|
FILETIME ftDelta;
|
|
__int64 i64Delta;
|
|
};
|
|
|
|
GetSystemTimeAsFileTime(&ftNow);
|
|
|
|
i64Delta = ftNow.dwHighDateTime;
|
|
i64Delta = i64Delta << 32;
|
|
i64Delta += ftNow.dwLowDateTime;
|
|
|
|
// Add the offset into the original time to get us a new time to check
|
|
i64Offset = FILETIME_SECOND;
|
|
i64Offset *= TIME_DELTA_SECONDS;
|
|
i64Delta += i64Offset;
|
|
|
|
lr = CertVerifyTimeValidity(&ftDelta, pcCertToTest->pCertInfo);
|
|
}
|
|
if(lr == 0)
|
|
fIgnoreTimeError = TRUE;
|
|
}
|
|
|
|
// End of delta checking
|
|
lr = WinVerifyTrust(hwnd, &guidAction, (void*)&data);
|
|
|
|
if(((LRESULT) lr) == CERT_E_REVOKED)
|
|
dwErr = CERT_VALIDITY_CERTIFICATE_REVOKED;
|
|
|
|
else if(((LRESULT) lr) == CERT_E_REVOCATION_FAILURE)
|
|
{
|
|
Assert(FALSE);
|
|
dwErr = CERT_VALIDITY_NO_CRL_FOUND;
|
|
}
|
|
else if (0 > lr) // WinVerifyTrust(hwnd, &guidAction, (void*)&data))
|
|
dwErr = CERT_VALIDITY_NO_TRUST_DATA;
|
|
|
|
if (dwErr)
|
|
DOUTL(DOUTL_CRYPT, "Trust provider returned 0x%.8lx", dwErr);
|
|
|
|
// Filter these out since the trust provider isn't.
|
|
if(fIgnoreTimeError)
|
|
dwErr &= ~(CERT_VALIDITY_BEFORE_START | CERT_VALIDITY_AFTER_END);
|
|
|
|
|
|
if(!(CheckCDPinCert(pMsg) && (dwErr == CERT_VALIDITY_NO_CRL_FOUND)))
|
|
dwErr &= ~dwToIgnore;
|
|
|
|
CertCloseStore(rgCAs[0], 0);
|
|
CertCloseStore(rgCAs[1], 0);
|
|
CertCloseStore(rgCAs[2], 0);
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
HRESULT CommonUI_ViewSigningProperties(HWND hwnd, PCCERT_CONTEXT pCert, HCERTSTORE hcMsg, UINT nStartPage)
|
|
{
|
|
CERT_VIEWPROPERTIES_STRUCT cvps;
|
|
TCHAR szTitle[CCHMAX_STRINGRES];
|
|
LPSTR oidPurpose = szOID_PKIX_KP_EMAIL_PROTECTION;
|
|
|
|
AthLoadString(idsSigningCertProperties, szTitle, ARRAYSIZE(szTitle));
|
|
|
|
memset((void*)&cvps, 0, sizeof(cvps));
|
|
|
|
cvps.dwSize = sizeof(cvps);
|
|
cvps.hwndParent = hwnd;
|
|
cvps.hInstance = g_hLocRes;
|
|
cvps.szTitle = szTitle;
|
|
cvps.pCertContext = pCert;
|
|
cvps.nStartPage = nStartPage;
|
|
cvps.arrayPurposes = &oidPurpose;
|
|
cvps.cArrayPurposes = 1;
|
|
cvps.cStores = hcMsg ? 1 : 0; // Count of other stores to search
|
|
cvps.rghstoreCAs = hcMsg ? &hcMsg : NULL; // Array of other stores to search
|
|
cvps.dwFlags = hcMsg ? CM_ADD_CERT_STORES : 0;
|
|
|
|
if(!((DwGetOption(OPT_REVOKE_CHECK) != 0) && !g_pConMan->IsGlobalOffline()))
|
|
cvps.dwFlags |= CRYPTDLG_REVOCATION_NONE;
|
|
else
|
|
cvps.dwFlags |= CRYPTDLG_REVOCATION_ONLINE;
|
|
|
|
return CertViewProperties(&cvps) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
HRESULT LoadResourceToHTMLStream(LPCTSTR szResName, IStream **ppstm)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Assert(ppstm);
|
|
|
|
hr = MimeOleCreateVirtualStream(ppstm);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// MIME header
|
|
// don't fail
|
|
(*ppstm)->Write(s_szHTMLMIME, sizeof(s_szHTMLMIME)-sizeof(TCHAR), NULL);
|
|
|
|
// HTML Header information
|
|
hr = HrLoadStreamFileFromResource(szResName, ppstm);
|
|
|
|
// If we didn't get the resource, lose the stream. The caller
|
|
// won't want it
|
|
if (FAILED(hr))
|
|
{
|
|
(*ppstm)->Release();
|
|
*ppstm = NULL;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
#ifdef YST
|
|
/***************************************************************************
|
|
|
|
Name : FreeCertArray
|
|
|
|
Purpose : Frees the array of certs returned by HrGetMyCerts.
|
|
|
|
Parameters: rgcc = array of cert contexts
|
|
ccc = count of cert contexts in rgcc
|
|
|
|
Returns : none
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
void FreeCertArray(PCCERT_CONTEXT * rgcc, ULONG ccc)
|
|
{
|
|
if (rgcc)
|
|
{
|
|
for (ULONG i = 0; i < ccc; i++)
|
|
if (rgcc[i])
|
|
CertFreeCertificateContext(rgcc[i]);
|
|
MemFree(rgcc);
|
|
}
|
|
}
|
|
|
|
#endif // YST
|
|
/***************************************************************************
|
|
|
|
Name : GetSignersEncryptionCert
|
|
|
|
Purpose : Gets the signer's encryption cert from the message
|
|
|
|
Parameters: pMsg -> Message Object
|
|
|
|
Returns : HRESULT - S_OK on success, MIME_E_SECURITY_NOCERT if no cert
|
|
|
|
Comment : Zero fills any return structures with no matching parameter
|
|
|
|
*************************************************************************/
|
|
|
|
HRESULT GetSignerEncryptionCert(IMimeMessage * pMsg, PCCERT_CONTEXT * ppcEncryptCert,
|
|
THUMBBLOB * ptbEncrypt, BLOB * pblSymCaps,
|
|
FILETIME * pftSigningTime)
|
|
{
|
|
DWORD cb;
|
|
HRESULT hr;
|
|
HCERTSTORE hcMsg = NULL;
|
|
DWORD i;
|
|
IMimeBody * pBody = NULL;
|
|
PCCERT_CONTEXT pccertEncrypt = NULL;
|
|
PCCERT_CONTEXT pccertSender = NULL;
|
|
THUMBBLOB tbTemp = {0, 0};
|
|
|
|
// Next 5 lines repeats in 3 others places of OE and we may have a separate function from then in future.
|
|
HBODY hBody = NULL;
|
|
SECSTATE SecState ={0};
|
|
|
|
if(FAILED(hr = HrGetSecurityState(pMsg, &SecState, &hBody)))
|
|
return(hr);
|
|
|
|
CleanupSECSTATE(&SecState);
|
|
|
|
Assert((ptbEncrypt != NULL) && (pblSymCaps != NULL) && (pftSigningTime != NULL));
|
|
|
|
// Init return structure
|
|
ptbEncrypt->pBlobData = NULL;
|
|
ptbEncrypt->cbSize = 0;
|
|
pblSymCaps->pBlobData = NULL;
|
|
pblSymCaps->cbSize = 0;
|
|
pftSigningTime->dwLowDateTime = 0;
|
|
pftSigningTime->dwHighDateTime = 0;
|
|
|
|
if (SUCCEEDED(hr = pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (void **)&pBody)))
|
|
{
|
|
PROPVARIANT var;
|
|
|
|
hr = MIME_E_SECURITY_NOCERT; // assume failure;
|
|
|
|
#ifdef _WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE_64, &var)))
|
|
{
|
|
hcMsg = (HCERTSTORE *) (var.pulVal);
|
|
}
|
|
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING_64, &var)))
|
|
{
|
|
Assert(VT_UI8 == var.vt);
|
|
|
|
pccertSender = (PCCERT_CONTEXT) (var.pulVal);
|
|
}
|
|
#else // !_WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE, &var)))
|
|
{
|
|
hcMsg = (HCERTSTORE) var.ulVal;
|
|
}
|
|
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING, &var)))
|
|
{
|
|
Assert(VT_UI4 == var.vt);
|
|
|
|
pccertSender = (PCCERT_CONTEXT) var.ulVal;
|
|
}
|
|
#endif // _WIN64
|
|
//
|
|
// Need to do a bit of work to identify the sender's encryption
|
|
// certificate.
|
|
//
|
|
// 1. Look for the id-aa-encryptKeyPref
|
|
// 2. Look for the Microsoft_Encryption_Key_Preference
|
|
// 3. If this is a sign only cert, look for a cert with the same
|
|
// issuer and subject
|
|
// 4. If this is a sign only cert, look for a cert with the same
|
|
// subject
|
|
//
|
|
|
|
if (hcMsg && SUCCEEDED(pBody->GetOption(OID_SECURITY_AUTHATTR, &var)))
|
|
{
|
|
PCRYPT_ATTRIBUTE pattr;
|
|
PCRYPT_ATTRIBUTES pattrs = NULL;
|
|
|
|
if (CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_Microsoft_Attribute_Sequence,
|
|
var.blob.pBlobData, var.blob.cbSize,
|
|
CRYPT_ENCODE_ALLOC_FLAG, NULL, &pattrs, &cb))
|
|
{
|
|
for (i=0, pattr = NULL; i < pattrs->cAttr; i++)
|
|
{
|
|
if (strcmp(pattrs->rgAttr[i].pszObjId,
|
|
szOID_Microsoft_Encryption_Cert) == 0)
|
|
{
|
|
PCRYPT_RECIPIENT_ID prid = NULL;
|
|
pattr = &pattrs->rgAttr[i];
|
|
if (CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_Microsoft_Encryption_Cert,
|
|
pattr->rgValue[0].pbData,
|
|
pattr->rgValue[0].cbData,
|
|
CRYPT_ENCODE_ALLOC_FLAG, NULL, &prid, &cb))
|
|
{
|
|
CERT_INFO certinfo;
|
|
certinfo.SerialNumber = prid->SerialNumber;
|
|
certinfo.Issuer = prid->Issuer;
|
|
pccertEncrypt = CertGetSubjectCertificateFromStore(
|
|
hcMsg, X509_ASN_ENCODING, &certinfo);
|
|
}
|
|
LocalFree(prid);
|
|
}
|
|
else if (strcmp(pattrs->rgAttr[i].pszObjId,
|
|
szOID_SMIME_Encryption_Key_Preference) == 0)
|
|
{
|
|
PSMIME_ENC_KEY_PREFERENCE pekp = NULL;
|
|
pattr = &pattrs->rgAttr[i];
|
|
if (CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_SMIME_Encryption_Key_Preference,
|
|
pattr->rgValue[0].pbData,
|
|
pattr->rgValue[0].cbData,
|
|
CRYPT_ENCODE_ALLOC_FLAG, NULL, &pekp, &cb))
|
|
{
|
|
pccertEncrypt = CertFindCertificateInStore(hcMsg,
|
|
X509_ASN_ENCODING, 0,
|
|
CERT_FIND_CERT_ID,
|
|
&pekp->RecipientId, NULL);
|
|
}
|
|
LocalFree(pekp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
LocalFree(pattrs);
|
|
}
|
|
|
|
MemFree(var.blob.pBlobData);
|
|
}
|
|
if ((pccertEncrypt == NULL) && (pccertSender != NULL))
|
|
{
|
|
DWORD dw;
|
|
HrGetCertKeyUsage(pccertSender, &dw);
|
|
if (!(dw & (CERT_KEY_ENCIPHERMENT_KEY_USAGE |
|
|
CERT_KEY_AGREEMENT_KEY_USAGE)))
|
|
{
|
|
pccertEncrypt = CertFindCertificateInStore(hcMsg,
|
|
X509_ASN_ENCODING, 0,
|
|
CERT_FIND_SUBJECT_NAME,
|
|
&pccertSender->pCertInfo->Subject, NULL);
|
|
while (pccertEncrypt != NULL) {
|
|
HrGetCertKeyUsage(pccertEncrypt, &dw);
|
|
if (dw & CERT_KEY_ENCIPHERMENT_KEY_USAGE) {
|
|
break;
|
|
}
|
|
|
|
pccertEncrypt = CertFindCertificateInStore(
|
|
hcMsg, X509_ASN_ENCODING, 0,
|
|
CERT_FIND_SUBJECT_NAME,
|
|
&pccertSender->pCertInfo->Subject,
|
|
pccertEncrypt);
|
|
}
|
|
}
|
|
else
|
|
pccertEncrypt = CertDuplicateCertificateContext(pccertSender);
|
|
}
|
|
|
|
if (pccertEncrypt == NULL)
|
|
goto error;
|
|
|
|
tbTemp.pBlobData =
|
|
(BYTE *)PVGetCertificateParam(pccertEncrypt, CERT_HASH_PROP_ID,
|
|
&tbTemp.cbSize);
|
|
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_SYMCAPS, &var)))
|
|
{
|
|
if (var.blob.cbSize) {
|
|
// we don't have to dupe the symcaps because we won't free
|
|
// the var's.
|
|
*pblSymCaps = var.blob;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_SIGNTIME, &var)))
|
|
{
|
|
if (var.filetime.dwLowDateTime != 0 || var.filetime.dwHighDateTime != 0) {
|
|
*pftSigningTime = var.filetime;
|
|
}
|
|
}
|
|
|
|
if (tbTemp.pBlobData && tbTemp.cbSize)
|
|
{
|
|
ptbEncrypt->cbSize = tbTemp.cbSize;
|
|
ptbEncrypt->pBlobData = tbTemp.pBlobData;
|
|
tbTemp.pBlobData = NULL;
|
|
}
|
|
|
|
if (ppcEncryptCert != NULL)
|
|
*ppcEncryptCert = CertDuplicateCertificateContext(pccertEncrypt);
|
|
hr = S_OK;
|
|
}
|
|
|
|
error:
|
|
SafeRelease(pBody);
|
|
if (tbTemp.pBlobData != NULL)
|
|
MemFree(tbTemp.pBlobData);
|
|
|
|
if (hcMsg != NULL)
|
|
CertCloseStore(hcMsg, 0);
|
|
|
|
if (pccertSender != NULL)
|
|
CertFreeCertificateContext(pccertSender);
|
|
if (pccertEncrypt != NULL)
|
|
CertFreeCertificateContext(pccertEncrypt);
|
|
return hr;
|
|
}
|
|
|
|
/***************************************************************************
|
|
|
|
Name : GetSigningCert
|
|
|
|
Purpose : Gets the signing cert from the message
|
|
|
|
Parameters: pMsg -> Message object
|
|
ppcSigningCert -> returned signing cert context. (optional)
|
|
Caller must CertFreeCertificateContext.
|
|
ptbSigner -> thumbprint blob.
|
|
Caller should supply the blob but must free the pbData.
|
|
pblSymCaps -> SymCaps blob.
|
|
Caller should supply the blob but must free the pbData.
|
|
pftSigningTime -> returned signing time
|
|
|
|
Returns : HRESULT - S_OK on success, MIME_E_SECURITY_NOCERT if no cert
|
|
|
|
Comment : Zero fills any return structures which have no matching
|
|
parameter.
|
|
|
|
***************************************************************************/
|
|
HRESULT GetSigningCert(IMimeMessage * pMsg, PCCERT_CONTEXT * ppcSigningCert, THUMBBLOB * ptbSigner, BLOB * pblSymCaps, FILETIME * pftSigningTime)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IMimeBody *pBody = NULL;
|
|
HBODY hBody = NULL;
|
|
|
|
Assert(ptbSigner && pblSymCaps && pftSigningTime);
|
|
|
|
// Init return structure
|
|
ptbSigner->pBlobData = NULL;
|
|
ptbSigner->cbSize = 0;
|
|
pblSymCaps->pBlobData = NULL;
|
|
pblSymCaps->cbSize = 0;
|
|
pftSigningTime->dwLowDateTime = 0;
|
|
pftSigningTime->dwHighDateTime = 0;
|
|
|
|
if(FAILED(hr = HrGetInnerLayer(pMsg, &hBody)))
|
|
return(hr);
|
|
|
|
if (SUCCEEDED(hr = pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (void**)&pBody)))
|
|
{
|
|
PROPVARIANT var;
|
|
PCCERT_CONTEXT pcSigningCert;
|
|
THUMBBLOB tbTemp = {0,0};
|
|
|
|
hr = MIME_E_SECURITY_NOCERT; // assume failure
|
|
|
|
#ifdef _WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING_64, &var)))
|
|
{
|
|
Assert(VT_UI8 == var.vt);
|
|
|
|
pcSigningCert = (PCCERT_CONTEXT)(var.pulVal);
|
|
#else // !_WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING, &var)))
|
|
{
|
|
Assert(VT_UI4 == var.vt);
|
|
|
|
pcSigningCert = (PCCERT_CONTEXT) var.ulVal;
|
|
#endif // _WIN64
|
|
if (pcSigningCert)
|
|
{
|
|
// Get the thumbprint
|
|
tbTemp.pBlobData = (BYTE *)PVGetCertificateParam(pcSigningCert, CERT_HASH_PROP_ID, &tbTemp.cbSize);
|
|
if (tbTemp.pBlobData && tbTemp.cbSize)
|
|
{
|
|
// Allocate return buffer
|
|
if (! MemAlloc((LPVOID *)&ptbSigner->pBlobData, tbTemp.cbSize))
|
|
hr = ResultFromScode(E_OUTOFMEMORY);
|
|
else
|
|
{
|
|
ptbSigner->cbSize = tbTemp.cbSize;
|
|
memcpy(ptbSigner->pBlobData, tbTemp.pBlobData, tbTemp.cbSize);
|
|
|
|
MemFree(tbTemp.pBlobData);
|
|
|
|
hr = S_OK;
|
|
|
|
// Have a thumbprint. Go get the symcaps and signing time
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_SYMCAPS, &var)))
|
|
{
|
|
Assert(VT_BLOB == var.vt);
|
|
|
|
*pblSymCaps = var.blob;
|
|
|
|
// Have a symcaps. Go get the signing time
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_SIGNTIME, &var)))
|
|
{
|
|
Assert(VT_FILETIME == var.vt);
|
|
|
|
*pftSigningTime = var.filetime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ppcSigningCert)
|
|
*ppcSigningCert = pcSigningCert; // Let caller free it.
|
|
else
|
|
CertFreeCertificateContext(pcSigningCert);
|
|
}
|
|
}
|
|
}
|
|
|
|
SafeRelease(pBody);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : HrSaveCACerts
|
|
|
|
Purpose : Add the messages CA certs to the CA store
|
|
|
|
Parameters: hcCA = CA system cert store
|
|
hcMsg = message cert store
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT HrSaveCACerts(HCERTSTORE hcCA, HCERTSTORE hcMsg)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PCCERT_CONTEXT pccert = NULL;
|
|
PCCERT_CONTEXT pccertSubject;
|
|
PCERT_EXTENSION pext;
|
|
|
|
// Verify good parameters exist
|
|
|
|
if ((hcCA == NULL) || (hcMsg == NULL))
|
|
{
|
|
Assert((hcCA != NULL) && (hcMsg != NULL));
|
|
goto error;
|
|
}
|
|
|
|
//
|
|
// The logic we are going to following to determine if we should be adding
|
|
// a certificate to the CA store is as follows:
|
|
//
|
|
// 1. If the basic constraints extension exists, and says its a CA then add
|
|
// to the CA store. Note that the converse is not true as NT4 Cert Server
|
|
// generated CA certs with the end-entity version of basic contraints.
|
|
// 2. If the certificate's subject is matched by the issuer of antoher cert in
|
|
// the store then it goes into the CA cert store.
|
|
//
|
|
// Note: There are some certificates in the store which may not get added to
|
|
// the CA store and are not. If the basic constraints extension is
|
|
// missing, but no issued cert is included in the message then it will
|
|
// be dropped on the floor. This case should not matter in practice.
|
|
//
|
|
|
|
while (pccert = CertEnumCertificatesInStore(hcMsg, pccert))
|
|
{
|
|
pext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
|
|
pccert->pCertInfo->cExtension,
|
|
pccert->pCertInfo->rgExtension);
|
|
if (pext != NULL)
|
|
{
|
|
; // M00TODO
|
|
}
|
|
|
|
pccertSubject = CertFindCertificateInStore(hcMsg, X509_ASN_ENCODING, 0,
|
|
CERT_FIND_ISSUER_NAME,
|
|
&pccert->pCertInfo->Subject, NULL);
|
|
if (pccertSubject != NULL)
|
|
{
|
|
if (!CertAddCertificateContextToStore(hcCA, pccert,
|
|
CERT_STORE_ADD_USE_EXISTING, NULL))
|
|
// Don't really fail
|
|
DebugTrace("CertAddCertificateContextToStore -> %x\n", GetLastError());
|
|
CertFreeCertificateContext(pccertSubject);
|
|
}
|
|
|
|
}
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : IsThumbprintInMVPBin
|
|
|
|
Purpose : Check the PR_USER_X509_CERTIFICATE prop for this thumbprint
|
|
|
|
Parameters: spv = prop value structure of PR_USER_X509_CERTIFICATE
|
|
lpThumbprint -> THUMBBLOB structure to find
|
|
lpIndex -> Returned index in MVP (or NULL)
|
|
pblSymCaps -> symcaps blob to fill in (or NULL)
|
|
lpftSigningTime -> returned signing time (or NULL)
|
|
lpfDefault -> returned default flag (or NULL)
|
|
|
|
Returns : TRUE if found
|
|
|
|
Comment : Note that the values returned in pblSymCaps and lpftSigningTime
|
|
are only valid if TRUE is returned.
|
|
|
|
***************************************************************************/
|
|
BOOL IsThumbprintInMVPBin(SPropValue spv, THUMBBLOB * lpThumbprint, ULONG * lpIndex,
|
|
BLOB * pblSymCaps, FILETIME * lpftSigningTime, BOOL * lpfDefault)
|
|
{
|
|
ULONG cValues, i;
|
|
LPSBinary lpsb = NULL;
|
|
CERTTAGS UNALIGNED *lpCurrentTag = NULL;
|
|
CERTTAGS UNALIGNED *lpTempTag;
|
|
LPBYTE lpbTagEnd;
|
|
BOOL fFound = FALSE;
|
|
|
|
// Initialize the return data
|
|
if (lpIndex)
|
|
*lpIndex = (ULONG)-1;
|
|
if (lpftSigningTime)
|
|
lpftSigningTime->dwLowDateTime = lpftSigningTime->dwHighDateTime = 0;
|
|
if (pblSymCaps)
|
|
{
|
|
pblSymCaps->cbSize = 0;
|
|
pblSymCaps->pBlobData = 0;
|
|
}
|
|
|
|
if (! PROP_ERROR((spv)))
|
|
{
|
|
lpsb = spv.Value.MVbin.lpbin;
|
|
cValues = spv.Value.MVbin.cValues;
|
|
|
|
// Check for duplicates
|
|
for (i = 0; i < cValues; i++)
|
|
{
|
|
lpCurrentTag = (LPCERTTAGS)lpsb[i].lpb;
|
|
lpbTagEnd = (LPBYTE)lpCurrentTag + lpsb[i].cb;
|
|
|
|
// Init the return structures
|
|
if (lpftSigningTime)
|
|
lpftSigningTime->dwLowDateTime = lpftSigningTime->dwHighDateTime = 0;
|
|
if (pblSymCaps)
|
|
{
|
|
pblSymCaps->cbSize = 0;
|
|
pblSymCaps->pBlobData = 0;
|
|
}
|
|
if (lpfDefault)
|
|
*lpfDefault = FALSE;
|
|
|
|
while ((LPBYTE)lpCurrentTag < lpbTagEnd)
|
|
{
|
|
// Check if this is the tag that contains the thumbprint
|
|
if (CERT_TAG_THUMBPRINT == lpCurrentTag->tag)
|
|
{
|
|
if ((lpThumbprint->cbSize == lpCurrentTag->cbData - SIZE_CERTTAGS) &&
|
|
! memcmp(lpThumbprint->pBlobData, &lpCurrentTag->rgbData,
|
|
lpThumbprint->cbSize))
|
|
{
|
|
if (lpIndex)
|
|
*lpIndex = i;
|
|
fFound = TRUE;
|
|
}
|
|
}
|
|
if (lpfDefault && (CERT_TAG_DEFAULT == lpCurrentTag->tag))
|
|
memcpy(lpfDefault, &lpCurrentTag->rgbData, min(sizeof(*lpfDefault), lpCurrentTag->cbData));
|
|
if (lpftSigningTime && (CERT_TAG_SIGNING_TIME == lpCurrentTag->tag))
|
|
memcpy(lpftSigningTime, &lpCurrentTag->rgbData, min(sizeof(FILETIME), lpCurrentTag->cbData));
|
|
if (pblSymCaps && (CERT_TAG_SYMCAPS == lpCurrentTag->tag))
|
|
{
|
|
pblSymCaps->cbSize = lpCurrentTag->cbData - SIZE_CERTTAGS;
|
|
pblSymCaps->pBlobData = lpCurrentTag->rgbData;
|
|
}
|
|
|
|
lpTempTag = lpCurrentTag;
|
|
lpCurrentTag = (LPCERTTAGS)((BYTE*)lpCurrentTag + lpCurrentTag->cbData);
|
|
if (lpCurrentTag == lpTempTag)
|
|
{
|
|
DOUTL(DOUTL_CRYPT, "Bad CertTag in PR_USER_X509_CERTIFICATE");
|
|
break; // Safety valve, prevent infinite loop if bad data
|
|
}
|
|
}
|
|
|
|
if (fFound)
|
|
return(TRUE);
|
|
}
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : MatchCertificate
|
|
|
|
Purpose : Checks if a specific certificate is in the WAB entry
|
|
|
|
Parameters: lpAdrBook -> IADRBook object
|
|
lpWabal -> Wabal object (for allocators)
|
|
lpbEntryID -> EntryID of this entry
|
|
cbEntryID -> Size of EntryID
|
|
pSenderThumbprint -> THUMBBLOB structure to find
|
|
lppMailUser -> [optional] returned MailUser object
|
|
|
|
Returns : TRUE if a match is found
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
BOOL MatchCertificate(LPADRBOOK lpAdrBook,
|
|
LPWABAL lpWabal,
|
|
DWORD cbEntryID,
|
|
LPBYTE lpbEntryID,
|
|
THUMBBLOB * pSenderThumbprint,
|
|
LPMAILUSER * lppMailUser)
|
|
{
|
|
HRESULT hr;
|
|
LPMAILUSER lpMailUser = NULL;
|
|
ULONG ulObjectType;
|
|
ULONG ul;
|
|
LPSPropValue ppv = NULL;
|
|
BOOL fReturn = FALSE;
|
|
|
|
|
|
if (HR_FAILED(hr = lpAdrBook->OpenEntry(cbEntryID, (LPENTRYID)lpbEntryID, NULL, MAPI_MODIFY, &ulObjectType, (LPUNKNOWN *)&(lpMailUser))))
|
|
{
|
|
Assert(FALSE);
|
|
goto exit;
|
|
}
|
|
|
|
if (HR_FAILED(hr = lpMailUser->GetProps((LPSPropTagArray)&ptaCert, 0, &ul, &ppv)))
|
|
// Shouldn't happen, but if it does, we don't have a ppv
|
|
goto exit;
|
|
|
|
fReturn = IsThumbprintInMVPBin(ppv[0], pSenderThumbprint, NULL, NULL, NULL, NULL);
|
|
|
|
exit:
|
|
if (ppv)
|
|
lpWabal->FreeBuffer(ppv);
|
|
if (lpMailUser)
|
|
{
|
|
if (lppMailUser && fReturn)
|
|
*lppMailUser = lpMailUser;
|
|
else
|
|
lpMailUser->Release();
|
|
}
|
|
else if (lppMailUser)
|
|
*lppMailUser = NULL;
|
|
|
|
return(fReturn);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : InitPropertyRestriction
|
|
|
|
Purpose : Fills in the property restriction structure
|
|
|
|
Parameters: lpsres -> SRestriction to fill in
|
|
lpspv -> property value structure for this property restriction
|
|
|
|
Returns : none
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
void InitPropertyRestriction(LPSRestriction lpsres, LPSPropValue lpspv)
|
|
{
|
|
lpsres->rt = RES_PROPERTY; // Restriction type Property
|
|
lpsres->res.resProperty.relop = RELOP_EQ;
|
|
lpsres->res.resProperty.ulPropTag = lpspv->ulPropTag;
|
|
lpsres->res.resProperty.lpProp = lpspv;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : FreeProws
|
|
|
|
Purpose : Destroys an SRowSet structure.
|
|
|
|
Parameters: prows -> row set to free
|
|
|
|
Returns : none
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
void FreeProws(LPWABAL lpWabal, LPSRowSet prows)
|
|
{
|
|
register ULONG irow;
|
|
|
|
if (prows)
|
|
{
|
|
for (irow = 0; irow < prows->cRows; ++irow)
|
|
if (prows->aRow[irow].lpProps)
|
|
lpWabal->FreeBuffer(prows->aRow[irow].lpProps);
|
|
lpWabal->FreeBuffer(prows);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : AddPropToMVPString
|
|
|
|
Purpose : Add a property to a multi-valued binary property in a prop array
|
|
|
|
Parameters: lpWabal -> Wabal object with allocator functions
|
|
lpaProps -> array of properties
|
|
uPropTag = property tag for MVP
|
|
index = index in lpaProps of MVP
|
|
lpszNew -> new data string
|
|
fNoDuplicates = TRUE if we should do nothing on duplicate adds
|
|
fCaseSensitive = TRUE if the duplicate check should be case sensitive
|
|
|
|
Returns : HRESULT
|
|
S_DUPLICATE_FOUND if we didn't add because of a duplicate
|
|
|
|
Comment : Find the size of the existing MVP
|
|
Add in the size of the new entry
|
|
allocate new space
|
|
copy old to new
|
|
free old
|
|
copy new entry
|
|
point prop array LPSZ to the new space
|
|
increment cValues
|
|
|
|
|
|
Note: The new MVP memory is AllocMore'd onto the lpaProps
|
|
allocation. We will unlink the pointer to the old MVP array,
|
|
but this will be cleaned up when the prop array is freed.
|
|
|
|
***************************************************************************/
|
|
HRESULT AddPropToMVPString(LPWABAL lpWabal, LPSPropValue lpaProps, DWORD index, LPWSTR lpwszNew,
|
|
BOOL fNoDuplicates, BOOL fCaseSensitive)
|
|
{
|
|
SWStringArray UNALIGNED *lprgwszOld = NULL; // old SString array
|
|
LPWSTR *lppwszNew = NULL; // new prop array
|
|
LPWSTR *lppwszOld = NULL; // old prop array
|
|
ULONG cbMVP = 0;
|
|
ULONG cExisting = 0;
|
|
LPBYTE lpNewTemp = NULL;
|
|
HRESULT hResult = hrSuccess;
|
|
SCODE sc = SUCCESS_SUCCESS;
|
|
ULONG i;
|
|
ULONG cbNew;
|
|
|
|
cbNew = lpwszNew ? (lstrlenW(lpwszNew) + 1)*sizeof(*lpwszNew) : 0;
|
|
|
|
// Find the size of any existing MVP entries
|
|
if (PROP_ERROR(lpaProps[index]))
|
|
// Un-ERROR the property tag
|
|
lpaProps[index].ulPropTag = PROP_TAG(MV_FLAG|PT_UNICODE, PROP_ID(lpaProps[index].ulPropTag));
|
|
else
|
|
{
|
|
// point to the structure in the prop array.
|
|
lprgwszOld = &(lpaProps[index].Value.MVszW);
|
|
lppwszOld = lprgwszOld->lppszW;
|
|
|
|
cExisting = lprgwszOld->cValues;
|
|
cbMVP = cExisting * sizeof(LPWSTR);
|
|
|
|
// Check for duplicates
|
|
if (fNoDuplicates)
|
|
{
|
|
for (i = 0; i < cExisting; i++)
|
|
if (fCaseSensitive ? (! StrCmpW(lpwszNew, lppwszOld[i])) : (! StrCmpIW(lpwszNew, lppwszOld[i])))
|
|
{
|
|
DOUTL(DOUTL_CRYPT,"AddPropToMVPStringfound duplicate.\n");
|
|
return(S_DUPLICATE_FOUND);
|
|
}
|
|
}
|
|
}
|
|
|
|
// cbMVP now contains the current size of the MVP
|
|
cbMVP += sizeof(LPWSTR); // room in the MVP for another string pointer
|
|
|
|
|
|
// Allocate room for new MVP array
|
|
if (sc = lpWabal->AllocateMore(cbMVP, lpaProps, (LPVOID *)&lppwszNew))
|
|
{
|
|
DebugTrace("AddPropToMVPString allocation (%u) failed %x\n", cbMVP, sc);
|
|
hResult = ResultFromScode(sc);
|
|
return(hResult);
|
|
}
|
|
|
|
// If there are properties there already, copy them to our new MVP
|
|
for (i = 0; i < cExisting; i++)
|
|
// Copy this property value to the MVP
|
|
lppwszNew[i] = lppwszOld[i];
|
|
|
|
// Add the new property value
|
|
// Allocate room for it
|
|
if (cbNew)
|
|
{
|
|
if (sc = lpWabal->AllocateMore(cbNew, lpaProps, (LPVOID *)&(lppwszNew[i])))
|
|
{
|
|
DebugTrace("AddPropToMVPString allocation (%u) failed %x\n", cbNew, sc);
|
|
hResult = ResultFromScode(sc);
|
|
return(hResult);
|
|
}
|
|
StrCpyNW(lppwszNew[i], lpwszNew, cbNew / sizeof(WCHAR));
|
|
|
|
lpaProps[index].Value.MVszW.lppszW= lppwszNew;
|
|
lpaProps[index].Value.MVszW.cValues = cExisting + 1;
|
|
|
|
}
|
|
else
|
|
lppwszNew[i] = NULL;
|
|
|
|
return(hResult);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : AddPropToMVPBin
|
|
|
|
Purpose : Add a property to a multi-valued binary property in a prop array
|
|
|
|
Parameters: lpaProps -> array of properties
|
|
uPropTag = property tag for MVP
|
|
index = index in lpaProps of MVP
|
|
lpNew -> new data
|
|
cbNew = size of lpbNew
|
|
fNoDuplicates = TRUE if we should not add duplicates
|
|
|
|
Returns : HRESULT
|
|
S_DUPLICATE_FOUND if we didn't add because of a duplicate
|
|
|
|
Comment : Find the size of the existing MVP
|
|
Add in the size of the new entry
|
|
allocate new space
|
|
copy old to new
|
|
free old
|
|
copy new entry
|
|
point prop array lpbin the new space
|
|
increment cValues
|
|
|
|
|
|
Note: The new MVP memory is AllocMore'd onto the lpaProps
|
|
allocation. We will unlink the pointer to the old MVP array,
|
|
but this will be cleaned up when the prop array is freed.
|
|
|
|
***************************************************************************/
|
|
HRESULT AddPropToMVPBin(LPWABAL lpWabal, LPSPropValue lpaProps, DWORD index, LPVOID lpNew,
|
|
ULONG cbNew, BOOL fNoDuplicates)
|
|
{
|
|
SBinaryArray UNALIGNED * lprgsbOld = NULL;
|
|
SBinaryArray * lprgsbNew = NULL;
|
|
LPSBinary lpsbOld = NULL;
|
|
LPSBinary lpsbNew = NULL;
|
|
ULONG cbMVP = 0;
|
|
ULONG cExisting = 0;
|
|
LPBYTE lpNewTemp = NULL;
|
|
HRESULT hResult = hrSuccess;
|
|
SCODE sc = SUCCESS_SUCCESS;
|
|
ULONG i;
|
|
|
|
// Find the size of any existing MVP entries
|
|
if (PT_ERROR == PROP_TYPE(lpaProps[index].ulPropTag))
|
|
// Un-ERROR the property tag
|
|
lpaProps[index].ulPropTag = PROP_TAG(PT_MV_BINARY, PROP_ID(lpaProps[index].ulPropTag));
|
|
else
|
|
{
|
|
// point to the structure in the prop array.
|
|
lprgsbOld = &(lpaProps[index].Value.MVbin);
|
|
lpsbOld = lprgsbOld->lpbin;
|
|
|
|
cExisting = lprgsbOld->cValues;
|
|
|
|
// Check for duplicates
|
|
if (fNoDuplicates)
|
|
{
|
|
for (i = 0; i < cExisting; i++)
|
|
if (cbNew == lpsbOld[i].cb && !memcmp(lpNew, lpsbOld[i].lpb, cbNew))
|
|
{
|
|
DOUTL(DOUTL_CRYPT,"AddPropToMVPBin found duplicate.\n");
|
|
return(S_DUPLICATE_FOUND);
|
|
}
|
|
}
|
|
|
|
cbMVP = cExisting * sizeof(SBinary);
|
|
}
|
|
|
|
// cbMVP now contains the current size of the MVP
|
|
cbMVP += sizeof(SBinary); // room in the MVP for another Sbin
|
|
|
|
// Allocate room for new MVP
|
|
if (sc = lpWabal->AllocateMore(cbMVP, lpaProps, (LPVOID*)&lpsbNew))
|
|
{
|
|
DOUTL(DOUTL_CRYPT,"AddPropToMVPBin allocation (%u) failed %x\n", cbMVP, sc);
|
|
hResult = ResultFromScode(sc);
|
|
return(hResult);
|
|
}
|
|
|
|
// If there are properties there already, copy them to our new MVP
|
|
for (i = 0; i < cExisting; i++)
|
|
{
|
|
// Copy this property value to the MVP
|
|
lpsbNew[i].cb = lpsbOld[i].cb;
|
|
lpsbNew[i].lpb = lpsbOld[i].lpb;
|
|
}
|
|
|
|
// Add the new property value
|
|
// Allocate room for it
|
|
if (sc = lpWabal->AllocateMore(cbNew, lpaProps, (LPVOID*)&(lpsbNew[i].lpb)))
|
|
{
|
|
DOUTL(DOUTL_CRYPT,"AddPropToMVPBin allocation (%u) failed %x\n", cbNew, sc);
|
|
hResult = ResultFromScode(sc);
|
|
return(hResult);
|
|
}
|
|
|
|
lpsbNew[i].cb = cbNew;
|
|
CopyMemory(lpsbNew[i].lpb, lpNew, cbNew);
|
|
|
|
lpaProps[index].Value.MVbin.lpbin = lpsbNew;
|
|
lpaProps[index].Value.MVbin.cValues = cExisting + 1;
|
|
|
|
return(hResult);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : RemoveValueFromMVPBinByIndex
|
|
|
|
Purpose : Remove a value from a multi-valued binary property in a prop array
|
|
|
|
Parameters: lpaProps -> array of properties
|
|
cProps = number of props in lpaProps
|
|
PropIndex = index in lpaProps of MVP
|
|
ValueIndex = index in MVP of value to remove
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT RemovePropFromMVBinByIndex(LPSPropValue lpaProps, DWORD cProps, DWORD PropIndex, DWORD ValueIndex)
|
|
{
|
|
SBinaryArray UNALIGNED * lprgsb = NULL;
|
|
LPSBinary lpsb = NULL;
|
|
ULONG cbTest;
|
|
LPBYTE lpTest;
|
|
ULONG cExisting;
|
|
|
|
// Find the size of any existing MVP entries
|
|
if (PROP_ERROR(lpaProps[PropIndex]))
|
|
// Property value doesn't exist.
|
|
return(ResultFromScode(MAPI_W_PARTIAL_COMPLETION));
|
|
|
|
// point to the structure in the prop array.
|
|
lprgsb = &(lpaProps[PropIndex].Value.MVbin);
|
|
lpsb = lprgsb->lpbin;
|
|
|
|
cExisting = lprgsb->cValues;
|
|
Assert(ValueIndex < cExisting);
|
|
|
|
// Look for value
|
|
lpsb = &(lprgsb->lpbin[ValueIndex]);
|
|
|
|
// Decrment number of values
|
|
if (--lprgsb->cValues == 0)
|
|
// If there are none left, mark the prop as an error
|
|
lpaProps[PropIndex].ulPropTag = PROP_TAG(PT_ERROR, PROP_ID(lpaProps[PropIndex].ulPropTag));
|
|
else
|
|
// Copy the remaining entries down over it.
|
|
if (ValueIndex + 1 < cExisting) // Are there any higher entries to copy?
|
|
CopyMemory(lpsb, lpsb + 1, ((cExisting - ValueIndex) - 1) * sizeof(SBinary));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void DebugFileTime(FILETIME ft)
|
|
{
|
|
SYSTEMTIME st = {0};
|
|
TCHAR szBuffer[256];
|
|
|
|
FileTimeToSystemTime(&ft, &st);
|
|
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), "%02d/%02d/%04d %02d:%02d:%02d\n", st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond);
|
|
OutputDebugString(szBuffer);
|
|
}
|
|
#endif
|
|
|
|
/***************************************************************************
|
|
|
|
Name : HrAddCertToWabContact
|
|
|
|
Purpose : Update a certificate in a particular wab contact
|
|
|
|
Parameters: lpWabal -> WABAL for WAB function access
|
|
lpAdrBook -> WAB ADRBOOK object
|
|
cbEntryID = size of lpEntryID
|
|
lpEntryID = entry id to operate on
|
|
pThumbprint -> certificate thumbprint
|
|
szEmailAddress -> email address to search for (optional)
|
|
szCertEmailAddress -> email address from the cert
|
|
pblSymCaps -> symcap blob (will be calculated if thumbprint is NULL)
|
|
ftSigningTime = signing time (will be calculated if thumbprint is NULL)
|
|
dwFlags = WFF_SHOWUI (we are allowed to show UI)
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : dwFlags is currently ignored
|
|
|
|
***************************************************************************/
|
|
HRESULT HrAddCertToWabContact(HWND hwnd, LPWABAL lpWabal, LPADRBOOK lpAdrBook, ULONG cbEntryID, LPBYTE lpEntryID,
|
|
THUMBBLOB * pThumbprint, LPWSTR lpwszEmailAddress, LPWSTR lpwszCertEmailAddress, BLOB *pblSymCaps,
|
|
FILETIME ftSigningTime, DWORD dwFlags)
|
|
{
|
|
HRESULT hr;
|
|
ULONG ulObjectType;
|
|
LPMAILUSER lpMailUser = NULL;
|
|
ULONG cProps = 0;
|
|
LPSPropValue ppv = NULL;
|
|
ULONG MVPindex;
|
|
BOOL fExistingSymCaps,
|
|
fMsgSymCaps,
|
|
fExistingSigningTime,
|
|
fMsgSigningTime,
|
|
fExists,
|
|
fExisted,
|
|
fDefault = FALSE,
|
|
fNewerThanExistingTime,
|
|
fNoCert;
|
|
BLOB blExistingSymCaps = {0};
|
|
FILETIME ftExistingSigningTime;
|
|
UNALIGNED BYTE * lpCertProp = NULL;
|
|
ULONG cbCertProp;
|
|
|
|
Assert(lpWabal);
|
|
Assert(lpEntryID);
|
|
Assert(pThumbprint);
|
|
Assert(lpwszEmailAddress || lpwszCertEmailAddress);
|
|
|
|
hr = lpAdrBook->OpenEntry(cbEntryID, (LPENTRYID)lpEntryID, NULL, MAPI_MODIFY, &ulObjectType, (LPUNKNOWN *)&(lpMailUser));
|
|
if (HR_FAILED(hr))
|
|
goto exit;
|
|
|
|
hr = lpMailUser->GetProps((LPSPropTagArray)&ptaResolve, 0, &cProps, &ppv);
|
|
if (HR_FAILED(hr) || ! cProps || ! ppv || PROP_ERROR(ppv[0]))
|
|
goto exit;
|
|
|
|
// Do we need to remove the existing value? Only if it has the same
|
|
// thumbprint, has a sMimeCapability and a signing time < the signing time
|
|
// input and the sMIMEcapability is different.
|
|
|
|
// The returned data is not reallocated, but the blobs point into the property data
|
|
fNoCert = PROP_ERROR(ppv[irsPR_USER_X509_CERTIFICATE]);
|
|
|
|
fExisted = fExists = IsThumbprintInMVPBin(ppv[irsPR_USER_X509_CERTIFICATE], pThumbprint, &MVPindex,
|
|
&blExistingSymCaps, &ftExistingSigningTime, &fDefault);
|
|
|
|
if (fExists)
|
|
{
|
|
// Create a bunch of flags to aid in deciding when to replace a cert and when to add one.
|
|
fExistingSymCaps = blExistingSymCaps.cbSize;
|
|
fMsgSymCaps = pblSymCaps && pblSymCaps->cbSize;
|
|
fExistingSigningTime = ftExistingSigningTime.dwLowDateTime || ftExistingSigningTime.dwHighDateTime;
|
|
fMsgSigningTime = ftSigningTime.dwLowDateTime || ftSigningTime.dwHighDateTime;
|
|
|
|
#ifdef DEBUG
|
|
DebugFileTime(ftSigningTime);
|
|
DebugFileTime(ftExistingSigningTime);
|
|
#endif
|
|
|
|
fNewerThanExistingTime = (CompareFileTime(&ftSigningTime, &ftExistingSigningTime) > 0);
|
|
|
|
if (fExists && fMsgSymCaps &&
|
|
(! fExistingSymCaps ||
|
|
fMsgSigningTime && !fExistingSigningTime ||
|
|
fMsgSigningTime && fExistingSigningTime && fNewerThanExistingTime))
|
|
{
|
|
RemovePropFromMVBinByIndex(ppv, cProps, irsPR_USER_X509_CERTIFICATE, MVPindex);
|
|
fExists = FALSE;
|
|
}
|
|
}
|
|
|
|
if (!fExists)
|
|
{
|
|
// Build up the PR_USER_X509_CERTIFICATE data
|
|
if (HR_FAILED(hr = HrBuildCertSBinaryData(fNoCert || (fExisted && fDefault), pThumbprint, pblSymCaps,
|
|
ftSigningTime, &lpCertProp, &cbCertProp)))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
// Add the new thumbprint to PR_USER_X509_CERTIFICATE
|
|
if (HR_FAILED(hr = AddPropToMVPBin(lpWabal, ppv, irsPR_USER_X509_CERTIFICATE, lpCertProp, cbCertProp, TRUE)))
|
|
goto exit;
|
|
|
|
// Make sure that the e-mail addresses are in this contact.
|
|
// NOTE: Add szEmailAddress BEFORE szCertEmailAddress!
|
|
if (lpwszEmailAddress)
|
|
{
|
|
if (! AddPropToMVPString(lpWabal, ppv, irsPR_CONTACT_EMAIL_ADDRESSES, lpwszEmailAddress, TRUE, FALSE))
|
|
// If we succeeded in adding an email address, we must match it with an
|
|
// address type.
|
|
AddPropToMVPString(lpWabal, ppv, irsPR_CONTACT_ADDRTYPES, (LPWSTR)c_wszSMTP, FALSE, FALSE);
|
|
// Don't care on failure
|
|
}
|
|
|
|
if (lpwszCertEmailAddress)
|
|
{
|
|
if (! AddPropToMVPString(lpWabal, ppv, irsPR_CONTACT_EMAIL_ADDRESSES, lpwszCertEmailAddress, TRUE, FALSE))
|
|
// If we succeeded in adding an email address, we must match it with an address type.
|
|
AddPropToMVPString(lpWabal, ppv, irsPR_CONTACT_ADDRTYPES, (LPWSTR)c_wszSMTP, FALSE, FALSE);
|
|
// Don't care on failure
|
|
}
|
|
|
|
// Make sure there is a PR_EMAIL_ADDRESS
|
|
if (PROP_ERROR(ppv[irsPR_EMAIL_ADDRESS]))
|
|
{
|
|
ppv[irsPR_EMAIL_ADDRESS].ulPropTag = PR_EMAIL_ADDRESS_W;
|
|
ppv[irsPR_EMAIL_ADDRESS].Value.lpszW = lpwszEmailAddress ? lpwszEmailAddress : lpwszCertEmailAddress;
|
|
}
|
|
|
|
// Make sure there is a PR_CONTACT_DEFAULT_ADDRESS_INDEX
|
|
if (PROP_ERROR(ppv[irsPR_CONTACT_DEFAULT_ADDRESS_INDEX]))
|
|
{
|
|
ppv[irsPR_CONTACT_DEFAULT_ADDRESS_INDEX].ulPropTag = PR_CONTACT_DEFAULT_ADDRESS_INDEX;
|
|
ppv[irsPR_CONTACT_DEFAULT_ADDRESS_INDEX].Value.ul = 0;
|
|
}
|
|
|
|
hr = lpMailUser->SetProps(cProps, ppv, NULL);
|
|
if (SUCCEEDED(hr))
|
|
hr = lpMailUser->SaveChanges(KEEP_OPEN_READWRITE);
|
|
if (HR_FAILED(hr))
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
SafeMemFree(lpCertProp);
|
|
|
|
if (ppv)
|
|
lpWabal->FreeBuffer(ppv);
|
|
ReleaseObj(lpMailUser);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : HrAddCertToWab
|
|
|
|
Purpose : Add or update a certificate in the wab
|
|
|
|
Parameters: hwnd = parent window handle
|
|
lpWabal -> WABAL for WAB function access
|
|
pThumbprint -> certificate thumbprint (optional)
|
|
pcCertContext -> certificate context (optional, if not supplied, we
|
|
will find it based on the pSenderThumbprint)
|
|
szEmailAddress -> email address to search for (optional)
|
|
szDisplayName -> display name for NEW contacts (optional)
|
|
pblSymCaps -> symcap blob (will be calculated if thumbprint is NULL)
|
|
ftSigningTime = signing time (will be calculated if thumbprint is NULL)
|
|
dwFlags = WFF_SHOWUI (we are allowed to show UI)
|
|
WFF_CREATE (we are allowed to create an entry if not found)
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment : Must have either pSenderThumbprint or pcCertContext.
|
|
|
|
This function will search the address book for all occurences of
|
|
the email addresses specified (szEmailAddress and the one in the cert)
|
|
and and will add or update the cert thumbprint and associated symcap
|
|
and signing time to each entry found.
|
|
|
|
***************************************************************************/
|
|
HRESULT HrAddCertToWab(HWND hwnd, LPWABAL lpWabal, THUMBBLOB *pThumbprint, PCCERT_CONTEXT pcCertContext,
|
|
LPWSTR lpwszEmailAddress, LPWSTR lpwszDisplayName, BLOB *pblSymCaps, FILETIME ftSigningTime, DWORD dwFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG ulObjectType;
|
|
ULONG cbWABEID;
|
|
LPENTRYID lpWABEID = NULL;
|
|
LPABCONT lpABCont = NULL;
|
|
LPMAPITABLE lpContentsTable = NULL;
|
|
LPSRowSet lpRow = NULL;
|
|
SRestriction res;
|
|
SRestriction resOr[4]; // array for OR restrictions
|
|
SPropValue propEmail1, propEmail2, propEmails1, propEmails2;
|
|
ULONG resCount;
|
|
LPADRBOOK lpAdrBook = NULL; // Don't Release!
|
|
HCERTSTORE hCertStore = NULL;
|
|
PCCERT_CONTEXT pcCertContextLocal = NULL;
|
|
LPWSTR pwszCertEmailAddress = NULL;
|
|
THUMBBLOB ThumbprintLocal = {0};
|
|
LPWABOBJECT lpWabObject;
|
|
LPMAILUSER lpMailUser = NULL;
|
|
ULONG cProps = 0;
|
|
LPSPropValue ppv = NULL;
|
|
ULONG ulRowCount = 0;
|
|
LPSTR pszCertEmail = NULL;
|
|
|
|
Assert(pcCertContext || pThumbprint);
|
|
if (! pcCertContext && !pThumbprint)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
|
|
// Get the email address from the cert
|
|
if (! pcCertContext)
|
|
{
|
|
hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER, c_szWABCertStore);
|
|
if (hCertStore)
|
|
{
|
|
// Find the cert in the store
|
|
if (pcCertContextLocal = CertFindCertificateInStore( hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_HASH, (void *)pThumbprint, NULL))
|
|
pcCertContext = pcCertContextLocal;
|
|
}
|
|
}
|
|
if (pcCertContext)
|
|
{
|
|
pszCertEmail = SzGetCertificateEmailAddress(pcCertContext); // in msoert
|
|
|
|
pwszCertEmailAddress = PszToUnicode(CP_ACP, pszCertEmail);
|
|
if (!pwszCertEmailAddress && pszCertEmail)
|
|
IF_NULLEXIT(NULL);
|
|
|
|
if (pcCertContextLocal)
|
|
CertFreeCertificateContext(pcCertContextLocal);
|
|
|
|
// Make sure we have a thumbprint
|
|
if (! pThumbprint)
|
|
{
|
|
ThumbprintLocal.pBlobData = (BYTE *)PVGetCertificateParam(pcCertContext, CERT_HASH_PROP_ID, &ThumbprintLocal.cbSize);
|
|
if (ThumbprintLocal.pBlobData)
|
|
pThumbprint = &ThumbprintLocal;
|
|
}
|
|
}
|
|
if (hCertStore)
|
|
CertCloseStore(hCertStore, 0);
|
|
|
|
// Must have an email address and a thumbprint
|
|
if (! (pwszCertEmailAddress || lpwszEmailAddress) || ! pThumbprint)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
// Get Address Book object
|
|
if (! (lpAdrBook = lpWabal->GetAdrBook())) // Don't release this!
|
|
{
|
|
Assert(lpAdrBook);
|
|
goto exit;
|
|
}
|
|
|
|
// Open the contents table on the local WAB. This will be a time hit with a large WAB.
|
|
if (HR_FAILED(hr = lpAdrBook->GetPAB(&cbWABEID, &lpWABEID)))
|
|
goto exit; // extremely unlikely failure
|
|
|
|
if (HR_FAILED(hr = lpAdrBook->OpenEntry(cbWABEID, lpWABEID, NULL, 0, &ulObjectType, (LPUNKNOWN *)&lpABCont)))
|
|
goto exit;
|
|
|
|
hr = lpABCont->GetContentsTable((WAB_PROFILE_CONTENTS|MAPI_UNICODE), &lpContentsTable);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Set the column set
|
|
hr = lpContentsTable->SetColumns((LPSPropTagArray)&ptaResolve, 0);
|
|
if (HR_FAILED(hr))
|
|
goto exit;
|
|
|
|
// Set up the property values for restrictions
|
|
// At least ONE of these will be filled in.
|
|
if (pwszCertEmailAddress)
|
|
{
|
|
propEmail1.ulPropTag = PR_EMAIL_ADDRESS_W;
|
|
propEmail1.Value.lpszW = pwszCertEmailAddress;
|
|
propEmails1.ulPropTag = PR_CONTACT_EMAIL_ADDRESSES_W;
|
|
propEmails1.Value.MVszW.cValues = 1;
|
|
propEmails1.Value.MVszW.lppszW = &pwszCertEmailAddress;
|
|
}
|
|
if (lpwszEmailAddress)
|
|
{
|
|
propEmail2.ulPropTag = PR_EMAIL_ADDRESS_W;
|
|
propEmail2.Value.lpszW = lpwszEmailAddress;
|
|
propEmails2.ulPropTag = PR_CONTACT_EMAIL_ADDRESSES_W;
|
|
propEmails2.Value.MVszW.cValues = 1;
|
|
propEmails2.Value.MVszW.lppszW = &lpwszEmailAddress;
|
|
}
|
|
|
|
resCount = 0;
|
|
res.rt = RES_OR;
|
|
res.res.resOr.lpRes = resOr;
|
|
|
|
if (pwszCertEmailAddress)
|
|
{
|
|
// PR_CONTACT_EMAIL_ADDRESSES match for cert email address
|
|
resOr[resCount].rt = RES_CONTENT;
|
|
resOr[resCount].res.resContent.ulFuzzyLevel = FL_IGNORECASE | FL_FULLSTRING;
|
|
resOr[resCount].res.resContent.ulPropTag = PR_CONTACT_EMAIL_ADDRESSES_W;
|
|
resOr[resCount++].res.resContent.lpProp = &propEmails1;
|
|
|
|
// PR_EMAIL_ADDRESS for cert email address
|
|
InitPropertyRestriction(&(resOr[resCount++]), &propEmail1);
|
|
}
|
|
|
|
if (lpwszEmailAddress && (!pwszCertEmailAddress || StrCmpIW(lpwszEmailAddress, pwszCertEmailAddress)))
|
|
{
|
|
// PR_CONTACT_EMAIL_ADDRESSES match for specified email address
|
|
resOr[resCount].rt = RES_CONTENT;
|
|
resOr[resCount].res.resContent.ulFuzzyLevel = FL_IGNORECASE | FL_FULLSTRING;
|
|
resOr[resCount].res.resContent.ulPropTag = PR_CONTACT_EMAIL_ADDRESSES_W;
|
|
resOr[resCount++].res.resContent.lpProp = &propEmails2;
|
|
|
|
// PR_EMAIL_ADDRESS for specified email address
|
|
InitPropertyRestriction(&(resOr[resCount++]), &propEmail2);
|
|
}
|
|
Assert(resCount);
|
|
|
|
res.res.resOr.cRes = resCount;
|
|
|
|
// Perform the restriction.
|
|
if (HR_FAILED(hr = lpContentsTable->Restrict(&res, 0)))
|
|
goto exit;
|
|
|
|
// Find any matches?
|
|
if (HR_FAILED(hr = lpContentsTable->GetRowCount(0, &ulRowCount)))
|
|
goto exit;
|
|
}
|
|
|
|
if (ulRowCount)
|
|
{
|
|
Assert(lpContentsTable);
|
|
// For each one, update the cert properties.
|
|
do
|
|
{
|
|
if (lpRow)
|
|
{
|
|
FreeProws(lpWabal, lpRow);
|
|
lpRow = NULL;
|
|
}
|
|
lpContentsTable->QueryRows(1, 0, &lpRow);
|
|
if (lpRow)
|
|
{
|
|
if (lpRow->cRows)
|
|
{
|
|
// Update the cert props for this contact
|
|
hr = HrAddCertToWabContact(hwnd, lpWabal, lpAdrBook,
|
|
lpRow->aRow[0].lpProps[irsPR_ENTRYID].Value.bin.cb,
|
|
lpRow->aRow[0].lpProps[irsPR_ENTRYID].Value.bin.lpb,
|
|
pThumbprint, lpwszEmailAddress, pwszCertEmailAddress,
|
|
pblSymCaps, ftSigningTime, dwFlags);
|
|
if (HR_FAILED(hr))
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
FreeProws(lpWabal, lpRow);
|
|
lpRow = NULL;
|
|
}
|
|
}
|
|
} while (lpRow);
|
|
|
|
}
|
|
else if (dwFlags & WFF_CREATE)
|
|
{
|
|
// Need to create a new entry and set it's properties
|
|
if (! (lpWabObject = lpWabal->GetWABObject())) // Don't release this!
|
|
{
|
|
Assert(lpWabObject);
|
|
hr = E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
|
|
hr = HrWABCreateEntry(lpAdrBook, lpWabObject, lpwszDisplayName, NULL, 0, &lpMailUser);
|
|
|
|
if (lpMailUser)
|
|
{
|
|
// Get the ENTRYID for this object
|
|
hr = lpMailUser->GetProps((LPSPropTagArray)&ptaEntryID, 0, &cProps, &ppv);
|
|
ReleaseObj(lpMailUser);
|
|
if (HR_FAILED(hr) || ! cProps || ! ppv || PROP_ERROR(ppv[0]))
|
|
goto exit;
|
|
|
|
// Update the cert props and email addresses for this contact
|
|
hr = HrAddCertToWabContact(hwnd, lpWabal, lpAdrBook, ppv[0].Value.bin.cb, ppv[0].Value.bin.lpb,
|
|
pThumbprint, lpwszEmailAddress, pwszCertEmailAddress, pblSymCaps, ftSigningTime, dwFlags);
|
|
|
|
if (HR_FAILED(hr))
|
|
goto exit;
|
|
}
|
|
}
|
|
else
|
|
hr = ResultFromScode(MAPI_E_NOT_FOUND);
|
|
|
|
exit:
|
|
if (ppv)
|
|
lpWabal->FreeBuffer(ppv);
|
|
if (lpRow)
|
|
FreeProws(lpWabal, lpRow);
|
|
if (lpWABEID)
|
|
lpWabal->FreeBuffer(lpWABEID);
|
|
ReleaseObj(lpABCont);
|
|
ReleaseObj(lpContentsTable);
|
|
MemFree(ThumbprintLocal.pBlobData);
|
|
MemFree(pwszCertEmailAddress);
|
|
MemFree(pszCertEmail);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
Name : HrAddSenderCertToWab
|
|
|
|
Purpose : Add or update a sender's certificate in the wab
|
|
|
|
Parameters: hwnd = parent window handle
|
|
pMsg -> mimeole msg
|
|
lpWabal -> wabal for this message (will be calculated if NULL)
|
|
pSenderThumbprint -> sender's thumbprint (will be calculated if NULL)
|
|
pblSymCaps -> symcap blob (will be calculated if thumbprint is NULL)
|
|
ftSigningTime = signing time (will be calculated if thumbprint is NULL)
|
|
dwFlags = WFF_SHOWUI (we are allowed to show UI)
|
|
WFF_CREATE (we are allowed to create an entry if
|
|
not found)
|
|
|
|
Returns : HRESULT
|
|
|
|
Comment :
|
|
|
|
***************************************************************************/
|
|
HRESULT HrAddSenderCertToWab(HWND hwnd, LPMIMEMESSAGE pMsg, LPWABAL lpWabal,
|
|
THUMBBLOB *pSenderThumbprint, BLOB *pblSymCaps,
|
|
FILETIME ftSigningTime, DWORD dwFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fFound;
|
|
ADRINFO rAdrInfo;
|
|
ULONG cProps;
|
|
THUMBBLOB tbThumbprint = {0};
|
|
BLOB blSymCaps = {0};
|
|
LPWABAL lpLocalWabal = NULL;
|
|
BOOL fLocalCert = FALSE;
|
|
CRYPT_HASH_BLOB hash;
|
|
HCERTSTORE hcsAddressBook = NULL;
|
|
HCERTSTORE hcsCA = NULL;
|
|
HCERTSTORE hcsMsg = NULL;
|
|
IMimeBody * pBody = NULL;
|
|
PCCERT_CONTEXT pcSignerCert = NULL;
|
|
PROPVARIANT var;
|
|
HBODY hBody = NULL;
|
|
|
|
// If we don't have all the required inputs, get them.
|
|
if (! pSenderThumbprint)
|
|
{
|
|
Assert(! pblSymCaps);
|
|
|
|
// Point the parameters to local blobs
|
|
pblSymCaps = &blSymCaps;
|
|
pSenderThumbprint = &tbThumbprint;
|
|
hr = GetSignerEncryptionCert(pMsg, NULL, pSenderThumbprint, pblSymCaps,
|
|
&ftSigningTime);
|
|
if (HR_FAILED(hr))
|
|
goto exit;
|
|
|
|
fLocalCert = TRUE;
|
|
|
|
if (! pSenderThumbprint || ! pSenderThumbprint->cbSize)
|
|
{
|
|
// No cert. Give it up.
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (! lpWabal)
|
|
{
|
|
hr = HrGetWabalFromMsg(pMsg, &lpLocalWabal);
|
|
if (HR_FAILED(hr))
|
|
goto exit;
|
|
lpWabal = lpLocalWabal;
|
|
}
|
|
|
|
if (!(lpWabal && pMsg && pSenderThumbprint))
|
|
{
|
|
AssertSz(pSenderThumbprint, "Null thumbprint");
|
|
hr = E_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
// Get the message certs into AddressBook and CA CAPI stores
|
|
if(FAILED(hr = HrGetInnerLayer(pMsg, &hBody)))
|
|
goto exit;
|
|
|
|
hr = pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (void **)&pBody);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hcsAddressBook = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER, c_szWABCertStore);
|
|
|
|
#ifdef _WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING_64, &var)))
|
|
{
|
|
pcSignerCert = (PCCERT_CONTEXT)(var.pulVal);
|
|
#else // !_WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING, &var)))
|
|
{
|
|
Assert(VT_UI4 == var.vt);
|
|
|
|
pcSignerCert = (PCCERT_CONTEXT) var.ulVal;
|
|
#endif // _WIN64
|
|
|
|
if (pcSignerCert)
|
|
{
|
|
if (hcsAddressBook)
|
|
{
|
|
CertAddCertificateContextToStore(hcsAddressBook, pcSignerCert, CERT_STORE_ADD_REPLACE_EXISTING, NULL);
|
|
}
|
|
CertFreeCertificateContext(pcSignerCert);
|
|
}
|
|
}
|
|
|
|
// Get the certbag property which contains the CA chain
|
|
#ifdef _WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE_64, &var)))
|
|
{
|
|
hcsMsg = (HCERTSTORE)(var.pulVal);
|
|
#else // !_WIN64
|
|
if (SUCCEEDED(pBody->GetOption(OID_SECURITY_HCERTSTORE, &var)))
|
|
{
|
|
hcsMsg = (HCERTSTORE) var.ulVal;
|
|
#endif // _WIN64
|
|
if (hcsMsg) // message store containing certs
|
|
{
|
|
// Add the CA certs
|
|
hcsCA = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL,
|
|
CERT_SYSTEM_STORE_CURRENT_USER, c_szCACertStore);
|
|
if (hcsCA)
|
|
{
|
|
HrSaveCACerts(hcsCA, hcsMsg);
|
|
CertCloseStore(hcsCA, 0);
|
|
}
|
|
|
|
// We have a thumbprint, we need to get that cert and add it to the
|
|
// address book store.
|
|
|
|
hash.cbData = pSenderThumbprint->cbSize;
|
|
hash.pbData = pSenderThumbprint->pBlobData;
|
|
pcSignerCert = CertFindCertificateInStore(hcsMsg, X509_ASN_ENCODING, 0,
|
|
CERT_FIND_SHA1_HASH, &hash, NULL);
|
|
if (pcSignerCert != NULL)
|
|
{
|
|
CertAddCertificateContextToStore(hcsAddressBook, pcSignerCert,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
NULL);
|
|
CertFreeCertificateContext(pcSignerCert);
|
|
}
|
|
|
|
CertCloseStore(hcsMsg, 0);
|
|
|
|
}
|
|
}
|
|
|
|
if (hcsAddressBook)
|
|
CertCloseStore(hcsAddressBook, 0);
|
|
SafeRelease(pBody);
|
|
}
|
|
|
|
fFound = lpWabal->FGetFirst(&rAdrInfo);
|
|
while (fFound)
|
|
{
|
|
// Get a sender (there may be more than one)
|
|
if (MAPI_ORIG == rAdrInfo.lRecipType && (rAdrInfo.lpwszDisplay || rAdrInfo.lpwszAddress))
|
|
{
|
|
hr = HrAddCertToWab(hwnd, lpWabal, pSenderThumbprint, NULL, rAdrInfo.lpwszAddress,
|
|
rAdrInfo.lpwszDisplay, pblSymCaps, ftSigningTime, dwFlags);
|
|
if (HR_FAILED(hr))
|
|
goto exit;
|
|
}
|
|
|
|
// Get the next address
|
|
fFound = lpWabal->FGetNext(&rAdrInfo);
|
|
} // while fFound
|
|
|
|
exit:
|
|
SafeRelease(lpLocalWabal);
|
|
if (fLocalCert)
|
|
{
|
|
if (tbThumbprint.pBlobData)
|
|
MemFree(tbThumbprint.pBlobData);
|
|
|
|
if (blSymCaps.pBlobData)
|
|
MemFree(blSymCaps.pBlobData);
|
|
}
|
|
|
|
if ((dwFlags & WFF_SHOWUI) && HR_FAILED(hr))
|
|
AthErrorMessageW(hwnd, MAKEINTRESOURCEW(idsAthenaMail), MAKEINTRESOURCEW(idsErrAddCertToWAB), hr);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
BOOL CertFilterFunction(PCCERT_CONTEXT pCertContext, LPARAM dwEmailAddr, DWORD, DWORD)
|
|
{
|
|
// return TRUE to show, FALSE to hide
|
|
BOOL fRet = TRUE;
|
|
ACCTFILTERINFO * pFilterInfo = (ACCTFILTERINFO *) dwEmailAddr;
|
|
|
|
PCCERT_CONTEXT *rgCertChain = NULL;
|
|
DWORD cCertChain = 0;
|
|
const DWORD dwIgnore = CERT_VALIDITY_NO_CRL_FOUND | CERT_VALIDITY_NO_TRUST_DATA;
|
|
LONG lRet;
|
|
|
|
// return TRUE to show, FALSE to hide
|
|
if(MatchCertEmailAddress(pCertContext, pFilterInfo->szEmail) == FALSE)
|
|
return FALSE;
|
|
|
|
DWORD dw = 0;
|
|
if(SUCCEEDED(HrGetCertKeyUsage(pCertContext, &dw)))
|
|
{
|
|
if(pFilterInfo->fEncryption)
|
|
{
|
|
if (!(dw & (CERT_KEY_ENCIPHERMENT_KEY_USAGE |
|
|
CERT_KEY_AGREEMENT_KEY_USAGE)))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!(dw & (CERT_DIGITAL_SIGNATURE_KEY_USAGE)))
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
DWORD dwErr = DwGenerateTrustedChain(NULL, NULL, pCertContext, dwIgnore, TRUE, &cCertChain, &rgCertChain);
|
|
if (rgCertChain)
|
|
{
|
|
for (cCertChain--; int(cCertChain) >= 0; cCertChain--)
|
|
CertFreeCertificateContext(rgCertChain[cCertChain]);
|
|
MemFree(rgCertChain);
|
|
}
|
|
|
|
if(dwErr != 0)
|
|
return(FALSE);
|
|
|
|
return(fRet);
|
|
}
|
|
|
|
int GetNumMyCertForAccount(HWND hwnd, IImnAccount * pAccount, BOOL fEncrypt, HCERTSTORE hc, PCCERT_CONTEXT * ppcSave)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG cCerts = 0;
|
|
PCCERT_CONTEXT pcCert = NULL;
|
|
TCHAR szAcctEmailAddress[CCHMAX_EMAIL_ADDRESS + 1] = "";
|
|
HCERTSTORE hcMy = NULL;
|
|
|
|
Assert(pAccount);
|
|
if(!hc)
|
|
{
|
|
hcMy = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_CURRENT_USER, c_szMyCertStore);
|
|
}
|
|
else
|
|
hcMy = hc;
|
|
|
|
if (!hcMy)
|
|
goto Exit;
|
|
|
|
// Is there a cert that I can use? If so, let's go associate it with the
|
|
// account and try again.
|
|
pAccount->GetPropSz(AP_SMTP_EMAIL_ADDRESS, szAcctEmailAddress, sizeof(szAcctEmailAddress));
|
|
|
|
// Enumerate all certs on this message
|
|
while (pcCert = CertEnumCertificatesInStore(hcMy, pcCert))
|
|
{
|
|
// Does this cert match our account?
|
|
if (MatchCertEmailAddress(pcCert, szAcctEmailAddress))
|
|
{
|
|
// Is it valid and trusted?
|
|
DWORD dwTrust;
|
|
PCCERT_CONTEXT *rgCertChain = NULL;
|
|
DWORD cCertChain = 0;
|
|
const DWORD dwIgnore = CERT_VALIDITY_NO_CRL_FOUND |
|
|
CERT_VALIDITY_NO_TRUST_DATA;
|
|
|
|
|
|
if(SUCCEEDED(HrGetCertKeyUsage(pcCert, &dwTrust)))
|
|
{
|
|
if(fEncrypt)
|
|
{
|
|
if (!(dwTrust & (CERT_KEY_ENCIPHERMENT_KEY_USAGE |
|
|
CERT_KEY_AGREEMENT_KEY_USAGE)))
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if(!(dwTrust & (CERT_DIGITAL_SIGNATURE_KEY_USAGE)))
|
|
continue;
|
|
}
|
|
}
|
|
|
|
dwTrust = DwGenerateTrustedChain(hwnd, NULL, pcCert, dwIgnore, TRUE, &cCertChain, &rgCertChain);
|
|
if (!dwTrust)
|
|
{
|
|
cCerts++;
|
|
if(ppcSave)
|
|
{
|
|
if (cCerts == 1)
|
|
*ppcSave = (PCERT_CONTEXT)CertDuplicateCertificateContext(pcCert);
|
|
else if (*ppcSave)
|
|
{
|
|
// more than one cert, get rid of the one we saved
|
|
CertFreeCertificateContext(*ppcSave);
|
|
*ppcSave = NULL;
|
|
}
|
|
}
|
|
}
|
|
// clean up the cert chain
|
|
if (rgCertChain)
|
|
{
|
|
for (cCertChain--; int(cCertChain) >= 0; cCertChain--)
|
|
CertFreeCertificateContext(rgCertChain[cCertChain]);
|
|
MemFree(rgCertChain);
|
|
}
|
|
}
|
|
}
|
|
Exit:
|
|
if((hc == NULL) && hcMy)
|
|
CertCloseStore(hcMy, 0);
|
|
|
|
return(cCerts);
|
|
}
|
|
|
|
HRESULT _HrFindMyCertForAccount(HWND hwnd, PCCERT_CONTEXT * ppcCertContext, IImnAccount * pAccount, BOOL fEncrypt)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG cCerts = 0;
|
|
HCERTSTORE hcMy = NULL;
|
|
PCCERT_CONTEXT pcSave = NULL;
|
|
TCHAR szAcctEmailAddress[CCHMAX_EMAIL_ADDRESS + 1] = "";
|
|
|
|
ACCTFILTERINFO FilterInfo;
|
|
|
|
Assert(pAccount);
|
|
|
|
hcMy = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, X509_ASN_ENCODING, NULL, CERT_SYSTEM_STORE_CURRENT_USER, c_szMyCertStore);
|
|
if (!hcMy)
|
|
{
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
cCerts = GetNumMyCertForAccount(hwnd, pAccount, fEncrypt, hcMy, &pcSave);
|
|
|
|
pAccount->GetPropSz(AP_SMTP_EMAIL_ADDRESS, szAcctEmailAddress, sizeof(szAcctEmailAddress));
|
|
|
|
if (cCerts > 1)
|
|
{
|
|
// Bring up a cert selector UI for the account
|
|
CERT_SELECT_STRUCT css;
|
|
LPTSTR lpTitle = NULL;
|
|
TCHAR szAcctName[CCHMAX_ACCOUNT_NAME + 1] = "";
|
|
TCHAR szTitleFormat[200] = "%1";
|
|
LPTSTR rgpsz[1] = {szAcctName};
|
|
|
|
memset(&css, 0, sizeof(css));
|
|
|
|
pcSave = NULL;
|
|
AthLoadString(fEncrypt ? idsSelectEncrCertTitle : idsSelectMyCertTitle, szTitleFormat, ARRAYSIZE(szTitleFormat));
|
|
|
|
pAccount->GetPropSz(AP_ACCOUNT_NAME, szAcctName, sizeof(szAcctName));
|
|
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_STRING |
|
|
FORMAT_MESSAGE_ARGUMENT_ARRAY, szTitleFormat, 0, 0,
|
|
(LPTSTR)&lpTitle, 0, (va_list *)rgpsz);
|
|
|
|
css.dwSize = sizeof(css);
|
|
css.hwndParent = hwnd;
|
|
css.hInstance = g_hInst;
|
|
css.szTitle = lpTitle;
|
|
css.arrayCertStore = &hcMy;
|
|
css.cCertStore = 1;
|
|
css.szPurposeOid = szOID_PKIX_KP_EMAIL_PROTECTION;
|
|
css.arrayCertContext = &pcSave;
|
|
css.cCertContext = 1;
|
|
FilterInfo.fEncryption = fEncrypt;
|
|
FilterInfo.dwFlags = 0;
|
|
FilterInfo.szEmail = szAcctEmailAddress;
|
|
css.lCustData = (LPARAM)(&FilterInfo);
|
|
css.pfnFilter = CertFilterFunction;
|
|
|
|
if (CertSelectCertificate(&css) && pcSave)
|
|
cCerts = 1;
|
|
else
|
|
hr = MAPI_E_USER_CANCEL;
|
|
|
|
if (lpTitle)
|
|
LocalFree(lpTitle); // Note, this is allocated by WIN32 function: FormatMessage
|
|
|
|
}
|
|
else if (cCerts == 0)
|
|
// No matches.
|
|
if(fEncrypt)
|
|
hr = MIME_E_SECURITY_NOCERT;
|
|
else
|
|
hr = MIME_E_SECURITY_NOSIGNINGCERT;
|
|
|
|
if (cCerts == 1)
|
|
{
|
|
PCCERT_CONTEXT pcCertContext = NULL;
|
|
THUMBBLOB tbSender = {0, 0};
|
|
|
|
// Found a cert. Associate it with the account!
|
|
// Get the thumbprint
|
|
tbSender.pBlobData = (BYTE *)PVGetCertificateParam(pcSave, CERT_HASH_PROP_ID, &tbSender.cbSize);
|
|
if (tbSender.pBlobData && tbSender.cbSize)
|
|
{
|
|
hr = pAccount->SetProp((fEncrypt ? AP_SMTP_ENCRYPT_CERT: AP_SMTP_CERTIFICATE), tbSender.pBlobData, tbSender.cbSize);
|
|
hr = pAccount->SaveChanges();
|
|
}
|
|
|
|
SafeMemFree(tbSender.pBlobData);
|
|
if (ppcCertContext)
|
|
*ppcCertContext = pcSave;
|
|
else
|
|
CertFreeCertificateContext(pcSave);
|
|
}
|
|
|
|
Exit:
|
|
if (hcMy)
|
|
CertCloseStore(hcMy, 0);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
ULONG GetHighestEncryptionStrength(void)
|
|
{
|
|
static ULONG ulHighestStrength = 0;
|
|
|
|
if (! ulHighestStrength)
|
|
// we haven't figured it out yet. Ask MimeOle what's highest.
|
|
MimeOleAlgStrengthFromSMimeCap(NULL, 0, TRUE, &ulHighestStrength);
|
|
return(ulHighestStrength);
|
|
}
|
|
|
|
|
|
// Largest symcap is currently 0x4E with 3DES, RC2/128, RC2/64, DES, RC2/40 and SHA-1.
|
|
// You may want to bump up the size when FORTEZZA algorithms are supported.
|
|
#define CCH_BEST_SYMCAP 0x50
|
|
|
|
HRESULT HrGetHighestSymcaps(LPBYTE * ppbSymcap, LPULONG pcbSymcap)
|
|
{
|
|
HRESULT hr=S_OK;
|
|
LPVOID pvSymCapsCookie = NULL;
|
|
LPBYTE pbEncode = NULL;
|
|
ULONG cbEncode = 0;
|
|
DWORD dwBits;
|
|
// The MimeOleSMimeCapsFull call is quite expensive. The results are always
|
|
// the same during a session. (They can only change with software upgrade.)
|
|
// Cache the results here for better performance.
|
|
static BYTE szSaveBestSymcap[CCH_BEST_SYMCAP];
|
|
static ULONG cbSaveBestSymcap = 0;
|
|
|
|
if (cbSaveBestSymcap == 0)
|
|
{
|
|
// Init with no symcap gives max allowed by providers
|
|
hr = MimeOleSMimeCapInit(NULL, NULL, &pvSymCapsCookie);
|
|
if (FAILED(hr))
|
|
goto exit;
|
|
|
|
if (pvSymCapsCookie)
|
|
{
|
|
// Finish up with SymCaps
|
|
MimeOleSMimeCapsFull(pvSymCapsCookie, TRUE, FALSE, pbEncode, &cbEncode);
|
|
|
|
if (cbEncode)
|
|
{
|
|
if (! MemAlloc((LPVOID *)&pbEncode, cbEncode))
|
|
cbEncode = 0;
|
|
else
|
|
{
|
|
hr = MimeOleSMimeCapsFull(pvSymCapsCookie, TRUE, FALSE, pbEncode, &cbEncode);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Save this symcap in the static array for next time
|
|
// Only if we have room!
|
|
if (cbEncode <= CCH_BEST_SYMCAP)
|
|
{
|
|
cbSaveBestSymcap = min(sizeof(szSaveBestSymcap),cbEncode);
|
|
memcpy(szSaveBestSymcap, pbEncode, cbSaveBestSymcap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
SafeMemFree(pvSymCapsCookie);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// We have saved the best in the static array. Avoid the time intensive
|
|
// MimeOle query.
|
|
cbEncode = cbSaveBestSymcap;
|
|
if (! MemAlloc((LPVOID *)&pbEncode, cbEncode))
|
|
cbEncode = 0;
|
|
else
|
|
memcpy(pbEncode, szSaveBestSymcap, cbEncode);
|
|
}
|
|
|
|
exit:
|
|
if (! pbEncode)
|
|
{
|
|
// Hey, there should ALWAYS be at least RC2 (40 bit). What happened?
|
|
AssertSz(cbEncode, "MimeOleSMimeCapGetEncAlg gave us no encoding algorithm");
|
|
|
|
// Try to fix it up as best you can. Stick in the RC2 value.
|
|
cbEncode = cbRC2_40_ALGORITHM_ID;
|
|
if (MemAlloc((LPVOID *)&pbEncode, cbEncode))
|
|
{
|
|
memcpy(pbEncode, (LPBYTE)c_RC2_40_ALGORITHM_ID, cbEncode);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
if (cbEncode && pbEncode)
|
|
{
|
|
*pcbSymcap = cbEncode;
|
|
*ppbSymcap = pbEncode;
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
// Not in use anymore
|
|
#if 0
|
|
HRESULT ShowSecurityPopup(HWND hwnd, DWORD cmdID, POINT *pPoint, IMimeMessage *pMsg)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HMENU hMenu;
|
|
INT id;
|
|
BOOL fDisableCertMenus = TRUE;
|
|
IMimeBody *pRoot = NULL;
|
|
PROPVARIANT var;
|
|
TCHAR szT[CCHMAX_STRINGRES];
|
|
|
|
AssertSz(pMsg, "Didn't expect to get here without a pMsg.");
|
|
|
|
hMenu = LoadPopupMenu(IDR_SECURE_MESSAGE_POPUP);
|
|
if (hMenu)
|
|
{
|
|
if ((OECSECCMD_ENCRYPTED == cmdID))
|
|
{
|
|
// remove the edit-trust menu
|
|
RemoveMenu(hMenu, ID_EDIT_TRUST, MF_BYCOMMAND);
|
|
RemoveMenu(hMenu, ID_SEPARATOR_1, MF_BYCOMMAND);
|
|
AthLoadString(idsViewEncryptID, szT, ARRAYSIZE(szT));
|
|
ModifyMenu(hMenu, ID_DIGITAL_ID, MF_BYCOMMAND | MF_STRING, ID_ENCRYPT_ID, szT);
|
|
}
|
|
|
|
hr = pMsg->BindToObject(HBODY_ROOT, IID_IMimeBody, (void**)&pRoot);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
#ifdef _WIN64
|
|
DWORD dwOption = (OECSECCMD_ENCRYPTED == cmdID) ? OID_SECURITY_CERT_DECRYPTION_64 : OID_SECURITY_CERT_SIGNING_64;
|
|
hr = pRoot->GetOption(dwOption, &var);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(VT_UI8 == var.vt);
|
|
if ((PCCERT_CONTEXT )(var.pulVal))
|
|
{
|
|
fDisableCertMenus = FALSE;
|
|
CertFreeCertificateContext((PCCERT_CONTEXT)(var.pulVal));
|
|
}
|
|
}
|
|
#else // !_WIN64
|
|
DWORD dwOption = (OECSECCMD_ENCRYPTED == cmdID) ? OID_SECURITY_CERT_DECRYPTION : OID_SECURITY_CERT_SIGNING;
|
|
hr = pRoot->GetOption(dwOption, &var);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
Assert(VT_UI4 == var.vt);
|
|
if ((PCCERT_CONTEXT) var.ulVal)
|
|
{
|
|
fDisableCertMenus = FALSE;
|
|
CertFreeCertificateContext((PCCERT_CONTEXT) var.ulVal);
|
|
}
|
|
}
|
|
#endif // !_WIN64
|
|
}
|
|
|
|
if (fDisableCertMenus)
|
|
{
|
|
EnableMenuItem(hMenu, ID_DIGITAL_ID, MF_GRAYED);
|
|
EnableMenuItem(hMenu, ID_EDIT_TRUST, MF_GRAYED);
|
|
// EnableMenuItem(hMenu, ID_DIGITAL_ID, MF_GRAYED);
|
|
}
|
|
|
|
id = (INT)TrackPopupMenu(
|
|
hMenu,
|
|
TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,
|
|
pPoint->x,
|
|
pPoint->y,
|
|
0,
|
|
hwnd,
|
|
NULL);
|
|
|
|
// we have to use TPM_RETURNCMD here as we need to process the command-id before returning from this
|
|
// function, other wise trident will be confused about the object being clicked on.
|
|
switch (id)
|
|
{
|
|
case ID_SECURITY_PROPERTIES:
|
|
{
|
|
MSGPROP msgProp={0};
|
|
|
|
msgProp.hwndParent = hwnd;
|
|
msgProp.dwFlags = ARF_RECEIVED;
|
|
msgProp.pMsg=pMsg;
|
|
msgProp.fSecure = IsSecure(msgProp.pMsg);
|
|
if (msgProp.fSecure)
|
|
{
|
|
msgProp.mpStartPage = MP_SECURITY;
|
|
HrGetWabalFromMsg(msgProp.pMsg, &msgProp.lpWabal);
|
|
}
|
|
|
|
// This will prevent the (possibly wrong) attachment count from being shown in the properties dialog.
|
|
msgProp.fFromListView = TRUE;
|
|
|
|
hr = HrMsgProperties(&msgProp);
|
|
ReleaseObj(msgProp.lpWabal);
|
|
break;
|
|
}
|
|
|
|
case ID_DIGITAL_ID:
|
|
case ID_ENCRYPT_ID:
|
|
case ID_EDIT_TRUST:
|
|
{
|
|
if (pRoot)
|
|
{
|
|
HCERTSTORE hcMsg = 0;
|
|
#ifdef _WIN64
|
|
if (SUCCEEDED(hr = pRoot->GetOption(OID_SECURITY_HCERTSTORE_64, &var)))
|
|
{
|
|
if (var.vt == VT_UI8)
|
|
hcMsg = (HCERTSTORE)(var.pulVal);
|
|
}
|
|
if (SUCCEEDED(hr = pRoot->GetOption((ID_ENCRYPT_ID == id) ? OID_SECURITY_CERT_DECRYPTION_64 : OID_SECURITY_CERT_SIGNING_64, &var)))
|
|
{
|
|
Assert(VT_UI8 == var.vt);
|
|
if ((PCCERT_CONTEXT)(var.pulVal))
|
|
{
|
|
if (ID_EDIT_TRUST != id)
|
|
hr = CommonUI_ViewSigningCertificate(hwnd, (PCCERT_CONTEXT)(var.pulVal), hcMsg);
|
|
else
|
|
hr = CommonUI_ViewSigningCertificateTrust(hwnd, (PCCERT_CONTEXT)(var.pulVal), hcMsg);
|
|
CertFreeCertificateContext(*(PCCERT_CONTEXT *)(&(var.uhVal)));
|
|
}
|
|
}
|
|
#else // !_WIN64
|
|
if (SUCCEEDED(hr = pRoot->GetOption(OID_SECURITY_HCERTSTORE, &var)))
|
|
{
|
|
if (var.vt == VT_UI4)
|
|
hcMsg = (HCERTSTORE) var.ulVal;
|
|
}
|
|
if (SUCCEEDED(hr = pRoot->GetOption((ID_ENCRYPT_ID == id) ? OID_SECURITY_CERT_DECRYPTION : OID_SECURITY_CERT_SIGNING, &var)))
|
|
{
|
|
Assert(VT_UI4 == var.vt);
|
|
if ((PCCERT_CONTEXT) var.ulVal)
|
|
{
|
|
if (ID_EDIT_TRUST != id)
|
|
hr = CommonUI_ViewSigningCertificate(hwnd, (PCCERT_CONTEXT) var.ulVal, hcMsg);
|
|
else
|
|
hr = CommonUI_ViewSigningCertificateTrust(hwnd, (PCCERT_CONTEXT) var.ulVal, hcMsg);
|
|
CertFreeCertificateContext((PCCERT_CONTEXT) var.ulVal);
|
|
}
|
|
}
|
|
#endif // _WIN64
|
|
if (hcMsg)
|
|
CertCloseStore(hcMsg, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ID_HELP_SECURITY:
|
|
OEHtmlHelp(hwnd, c_szCtxHelpFileHTMLCtx, HH_DISPLAY_TOPIC, (DWORD_PTR)(LPCSTR)"mail_overview_send_secure_messages.htm");
|
|
break;
|
|
|
|
}
|
|
|
|
DestroyMenu(hMenu);
|
|
SafeRelease(pRoot);
|
|
} else
|
|
hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
#endif // 0
|
|
|
|
void ShowDigitalIDs(HWND hWnd)
|
|
{
|
|
CRYPTUI_CERT_MGR_STRUCT mgrCert;
|
|
|
|
mgrCert.dwSize = sizeof(mgrCert);
|
|
mgrCert.hwndParent = hWnd;
|
|
mgrCert.dwFlags = 0;
|
|
mgrCert.pwszTitle = NULL;
|
|
mgrCert.pszInitUsageOID = NULL;
|
|
|
|
CryptUIDlgCertMgr(&mgrCert);
|
|
return;
|
|
}
|
|
|
|
|
|
BOOL CheckCDPinCert(LPMIMEMESSAGE pMsg)
|
|
{
|
|
BOOL fRet;
|
|
IMimeBody *pBody;
|
|
HBODY hBody = NULL;
|
|
|
|
if(!pMsg)
|
|
return(FALSE);
|
|
|
|
fRet = FALSE;
|
|
|
|
if(FAILED(HrGetInnerLayer(pMsg, &hBody)))
|
|
return(FALSE);
|
|
|
|
|
|
if (SUCCEEDED(pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (void**)&pBody)))
|
|
{
|
|
PROPVARIANT var;
|
|
|
|
#ifdef _WIN64
|
|
if(SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING_64, &var)))
|
|
{
|
|
Assert(VT_UI8 == var.vt);
|
|
PCCERT_CONTEXT pcCert= (PCCERT_CONTEXT)(var.pulVal);
|
|
fRet = CheckCDPinCert(pcCert);
|
|
}
|
|
#else // !_WIN64
|
|
if(SUCCEEDED(pBody->GetOption(OID_SECURITY_CERT_SIGNING, &var)))
|
|
{
|
|
Assert(VT_UI4 == var.vt);
|
|
PCCERT_CONTEXT pcCert= (PCCERT_CONTEXT) var.ulVal;
|
|
fRet = CheckCDPinCert(pcCert);
|
|
}
|
|
#endif // _WIN64
|
|
|
|
pBody->Release();
|
|
}
|
|
|
|
return(fRet);
|
|
}
|
|
BOOL CheckCDPinCert(PCCERT_CONTEXT pcCert)
|
|
{
|
|
if (pcCert)
|
|
{
|
|
PCERT_EXTENSION pExt = CertFindExtension(szOID_CRL_DIST_POINTS, pcCert->pCertInfo->cExtension, pcCert->pCertInfo->rgExtension);
|
|
if(pExt != NULL)
|
|
return TRUE;
|
|
}
|
|
return(FALSE);
|
|
}
|
|
|
|
#ifdef YST
|
|
BOOL ParseNames(DWORD * pcNames, PCERT_NAME_BLOB * prgNames, HWND hwnd, DWORD idc)
|
|
{
|
|
DWORD cb;
|
|
DWORD cEntry = 0;
|
|
DWORD cNames = 0;
|
|
BOOL f;
|
|
DWORD i;
|
|
LPWSTR pwsz;
|
|
LPWSTR pwsz1;
|
|
CRYPT_DER_BLOB rgDer[50] = {0};
|
|
CERT_ALT_NAME_INFO rgNames[50] = {0};
|
|
CERT_ALT_NAME_ENTRY rgEntry[200] = {0};
|
|
WCHAR rgwch[4096];
|
|
|
|
GetDlgItemTextW(hwnd, idc, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
|
|
pwsz = rgwch;
|
|
|
|
while (*pwsz != 0) {
|
|
if (*pwsz == ' ') {
|
|
while (*pwsz == ' ') pwsz++;
|
|
rgNames[cNames-1].cAltEntry += 1;
|
|
}
|
|
else {
|
|
cNames += 1;
|
|
rgNames[cNames-1].rgAltEntry = &rgEntry[cEntry];
|
|
rgNames[cNames-1].cAltEntry = 1;
|
|
}
|
|
|
|
if (_wcsnicmp(pwsz, L"SMTP:", 5) == 0) {
|
|
pwsz += 5;
|
|
while (*pwsz == ' ') pwsz++;
|
|
rgEntry[cEntry].dwAltNameChoice = CERT_ALT_NAME_RFC822_NAME;
|
|
rgEntry[cEntry].pwszRfc822Name = pwsz;
|
|
while ((*pwsz != 0) && (*pwsz != '\n') && (*pwsz != '\r')) pwsz++;
|
|
}
|
|
else if (_wcsnicmp(pwsz, L"X500:", 5) == 0) {
|
|
pwsz += 5;
|
|
while (*pwsz == ' ') pwsz++;
|
|
for (pwsz1 = pwsz; ((*pwsz != 0) && (*pwsz != '\n') &&
|
|
(*pwsz != '\r')); pwsz++);
|
|
if (*pwsz != 0) {
|
|
*pwsz = 0;
|
|
pwsz++;
|
|
}
|
|
|
|
rgEntry[cEntry].dwAltNameChoice = CERT_ALT_NAME_DIRECTORY_NAME;
|
|
f = CertStrToNameW(X509_ASN_ENCODING, pwsz1,
|
|
CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG, NULL,
|
|
NULL, &cb, NULL);
|
|
if (!f) return FALSE;
|
|
|
|
rgEntry[cEntry].DirectoryName.pbData = (LPBYTE) malloc(cb);
|
|
f = CertStrToNameW(X509_ASN_ENCODING, pwsz1,
|
|
CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG, NULL,
|
|
rgEntry[cEntry].DirectoryName.pbData, &cb,
|
|
NULL);
|
|
if (!f) return FALSE;
|
|
rgEntry[cEntry].DirectoryName.cbData = cb;
|
|
}
|
|
else {
|
|
return FALSE;
|
|
}
|
|
|
|
if (*pwsz == '\r') {
|
|
*pwsz = 0;
|
|
pwsz++;
|
|
}
|
|
if (*pwsz == '\n') {
|
|
*pwsz = 0;
|
|
pwsz++;
|
|
}
|
|
cEntry += 1;
|
|
}
|
|
|
|
*prgNames = (PCERT_NAME_BLOB) malloc(sizeof(CERT_NAME_BLOB) * cNames);
|
|
if (*prgNames == NULL) return FALSE;
|
|
|
|
for (i=0; i<cNames; i++) {
|
|
f = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
|
|
&rgNames[i], CRYPT_ENCODE_ALLOC_FLAG, NULL,
|
|
&(*prgNames)[i].pbData, &(*prgNames)[i].cbData);
|
|
if (!f) return f;
|
|
}
|
|
*pcNames = cNames;
|
|
return f;
|
|
}
|
|
#endif // YST
|
|
|
|
#ifdef SMIME_V3
|
|
BOOL FNameInList(LPSTR szAddr, DWORD cReceiptFromList, CERT_NAME_BLOB *rgReceiptFromList)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
|
|
if (cReceiptFromList == 0)
|
|
{
|
|
fResult = TRUE;
|
|
}
|
|
else {
|
|
DWORD cb;
|
|
DWORD i;
|
|
DWORD i1;
|
|
char rgch[256];
|
|
|
|
for (i=0; !fResult && (i<cReceiptFromList); i++)
|
|
{
|
|
CERT_ALT_NAME_INFO * pname = NULL;
|
|
|
|
if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
|
|
rgReceiptFromList[i].pbData, rgReceiptFromList[i].cbData,
|
|
CRYPT_DECODE_ALLOC_FLAG,NULL,
|
|
&pname, &cb))
|
|
|
|
{
|
|
for (i1=0; !fResult && (i1<pname->cAltEntry); i1++)
|
|
{
|
|
switch (pname->rgAltEntry[i1].dwAltNameChoice)
|
|
{
|
|
case CERT_ALT_NAME_RFC822_NAME:
|
|
cb = WideCharToMultiByte(CP_ACP, 0,
|
|
(pname->rgAltEntry[i1]).pwszRfc822Name, -1,
|
|
rgch, sizeof(rgch), NULL, NULL);
|
|
|
|
Assert(cb < sizeof(rgch) - 2);
|
|
|
|
if (lstrcmpi(szAddr, rgch))
|
|
{
|
|
fResult = TRUE;
|
|
}
|
|
}
|
|
}
|
|
LocalFree(pname);
|
|
}
|
|
else
|
|
{
|
|
AssertSz(FALSE, "Bad Receipt From Name");
|
|
// $TODO - handle this error
|
|
}
|
|
}
|
|
}
|
|
return fResult;
|
|
}
|
|
|
|
|
|
// Return security label as Unicode text string
|
|
HRESULT HrGetLabelString(LPMIMEMESSAGE pMsg, LPWSTR *pwStr)
|
|
{
|
|
|
|
PCRYPT_ATTRIBUTE pattrLabel;
|
|
CRYPT_ATTR_BLOB valLabel;
|
|
LPBYTE pbLabel = NULL;
|
|
DWORD cbLabel;
|
|
PSMIME_SECURITY_LABEL plabel = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
IMimeSecurity2 * pSMIME3 = NULL;
|
|
IMimeBody *pBody = NULL;
|
|
HBODY hBody = NULL;
|
|
|
|
Assert(pMsg);
|
|
|
|
if(FAILED(hr = HrGetInnerLayer(pMsg, &hBody)))
|
|
return(hr);
|
|
|
|
if(pMsg->BindToObject(hBody ? hBody : HBODY_ROOT, IID_IMimeBody, (void **)&pBody) == S_OK)
|
|
{
|
|
if(pBody->QueryInterface(IID_IMimeSecurity2, (LPVOID *) &pSMIME3) == S_OK)
|
|
{
|
|
|
|
// Get label attribute
|
|
if(pSMIME3->GetAttribute(0, 0, SMIME_ATTRIBUTE_SET_SIGNED,
|
|
0, szOID_SMIME_Security_Label,
|
|
&pattrLabel) == S_OK)
|
|
{
|
|
// decode label
|
|
if(CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_SMIME_Security_Label,
|
|
pattrLabel->rgValue[0].pbData,
|
|
pattrLabel->rgValue[0].cbData,
|
|
CRYPT_DECODE_ALLOC_FLAG,
|
|
&CryptDecodeAlloc, &plabel, &cbLabel))
|
|
{
|
|
SpISMimePolicyLabelInfo spspli;
|
|
|
|
// Get the required interface to the policy module.
|
|
if(HrQueryPolicyInterface(0, plabel->pszObjIdSecurityPolicy, IID_ISMimePolicyLabelInfo,
|
|
(LPVOID *) &spspli) == S_OK)
|
|
{
|
|
|
|
LPWSTR pwchLabel = NULL;
|
|
// get label description string
|
|
if(spspli->GetStringizedLabel(0, plabel, &pwchLabel) == S_OK)
|
|
{
|
|
|
|
*pwStr = pwchLabel;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
hr = S_FALSE;
|
|
SafeMemFree(plabel);
|
|
}
|
|
}
|
|
SafeRelease(pSMIME3);
|
|
}
|
|
ReleaseObj(pBody);
|
|
}
|
|
return(hr);
|
|
}
|
|
#endif // SMIME_V3
|
|
|
|
HRESULT HrShowSecurityProperty(HWND hwnd, LPMIMEMESSAGE pMsg)
|
|
{
|
|
MSGPROP msgProp={0};
|
|
HRESULT hr = S_OK;
|
|
|
|
msgProp.hwndParent = hwnd;
|
|
msgProp.dwFlags = ARF_RECEIVED;
|
|
msgProp.pMsg = pMsg;
|
|
msgProp.fSecure = IsSecure(msgProp.pMsg);
|
|
if (msgProp.fSecure)
|
|
{
|
|
msgProp.mpStartPage = MP_SECURITY;
|
|
HrGetWabalFromMsg(msgProp.pMsg, &msgProp.lpWabal);
|
|
}
|
|
|
|
hr = HrMsgProperties(&msgProp);
|
|
ReleaseObj(msgProp.lpWabal);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
void CreateContentIdentifier(TCHAR *pchContentID, DWORD cchSize, LPMIMEMESSAGE pMsg)
|
|
{
|
|
SYSTEMTIME SysTime;
|
|
LPWSTR lpszSubj = NULL;
|
|
int nLen = 0;
|
|
TCHAR szTmp[21];
|
|
|
|
|
|
GetSystemTime(&SysTime);
|
|
MimeOleGetBodyPropW(pMsg, HBODY_ROOT, PIDTOSTR(PID_HDR_SUBJECT), NOFLAGS, &lpszSubj);
|
|
nLen = lstrlenW(lpszSubj);
|
|
|
|
for(int i =0; (i < 5) && (i < nLen); i++)
|
|
wnsprintf(szTmp + i*4, (ARRAYSIZE(szTmp) - i*4), "%04d", lpszSubj[i]);
|
|
|
|
szTmp[i*4] = _T('\0');
|
|
|
|
// ContentId is sequence of following text characters
|
|
// 1. Prefix ("797374" + "-" and code - 7 chars
|
|
// 2. System time + "-" - 18 chars
|
|
// 3. First 5 Unicode chars (or lstrlen) in dec of Subject - 20 chars
|
|
// Total # of chars 7 + 18 + 20 + 1 = 46
|
|
// if you change this, also change CONTENTID_SIZE
|
|
|
|
wnsprintf(pchContentID, cchSize, "%s-%4d%2d%1d%2d%2d%2d%2d%2d-%s",
|
|
sz_OEMS_ContIDPrefix,
|
|
SysTime.wYear,
|
|
SysTime.wMonth,
|
|
SysTime.wDayOfWeek,
|
|
SysTime.wDay,
|
|
SysTime.wHour,
|
|
SysTime.wMinute,
|
|
SysTime.wSecond,
|
|
SysTime.wMilliseconds,
|
|
szTmp);
|
|
|
|
SafeMimeOleFree(lpszSubj);
|
|
}
|