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.
445 lines
14 KiB
445 lines
14 KiB
#ifdef SMIME_V3
|
|
#include <windows.h>
|
|
#include <mimeole.h>
|
|
#include <essout.h>
|
|
|
|
#include "badstrfunctions.h"
|
|
|
|
#include "demand.h"
|
|
#include "crypttls.h"
|
|
#include "demand2.h"
|
|
|
|
extern CRYPT_DECODE_PARA CryptDecodeAlloc;
|
|
#define szOID_MSFT_ATTR_SEQUENCE "1.3.6.1.4.1.311.16.1.1"
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct {
|
|
DWORD cNames;
|
|
CERT_NAME_BLOB * rgNames;
|
|
} ReceiptNames;
|
|
|
|
HRESULT SetNames(ReceiptNames * pnames, DWORD cNames, CERT_NAME_BLOB * rgNames)
|
|
{
|
|
DWORD cb;
|
|
DWORD i;
|
|
LPBYTE pb;
|
|
|
|
if (pnames->rgNames != NULL) {
|
|
free(pnames->rgNames);
|
|
pnames->rgNames = NULL;
|
|
pnames->cNames = 0;
|
|
}
|
|
|
|
for (i=0, cb=cNames*sizeof(CERT_NAME_BLOB); i<cNames; i++) {
|
|
cb += rgNames[i].cbData;
|
|
}
|
|
|
|
pnames->rgNames = (CERT_NAME_BLOB *) malloc(cb);
|
|
if (pnames->rgNames == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pb = (LPBYTE) &pnames->rgNames[cNames];
|
|
for (i=0; i<cNames; i++) {
|
|
pnames->rgNames[i].pbData = pb;
|
|
pnames->rgNames[i].cbData = rgNames[i].cbData;
|
|
memcpy(pb, rgNames[i].pbData, rgNames[i].cbData);
|
|
pb += rgNames[i].cbData;
|
|
}
|
|
|
|
pnames->cNames = cNames;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT MergeNames(ReceiptNames * pnames, DWORD cNames, CERT_NAME_BLOB * rgNames)
|
|
{
|
|
DWORD cb;
|
|
DWORD i;
|
|
DWORD i1;
|
|
LPBYTE pb;
|
|
CERT_NAME_BLOB * p;
|
|
|
|
for (i=0, cb=0; i<pnames->cNames; i++) {
|
|
cb += pnames->rgNames[i].cbData;
|
|
}
|
|
|
|
for (i=0; i<cNames; i++) {
|
|
cb += rgNames[i].cbData;
|
|
}
|
|
|
|
p = (CERT_NAME_BLOB *) malloc(cb + (pnames->cNames + cNames) *
|
|
sizeof(CERT_NAME_BLOB));
|
|
if (p == NULL) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
pb = (LPBYTE) &p[pnames->cNames + cNames];
|
|
for (i=0, i1=0; i<pnames->cNames; i++, i1++) {
|
|
p[i1].pbData = pb;
|
|
p[i1].cbData = pnames->rgNames[i].cbData;
|
|
memcpy(pb, pnames->rgNames[i].pbData, pnames->rgNames[i].cbData);
|
|
pb += pnames->rgNames[i].cbData;
|
|
}
|
|
|
|
for (i=0; i<pnames->cNames; i++, i1++) {
|
|
p[i1].pbData = pb;
|
|
p[i1].cbData = rgNames[i].cbData;
|
|
memcpy(pb, rgNames[i].pbData, rgNames[i].cbData);
|
|
pb += rgNames[i].cbData;
|
|
}
|
|
|
|
free(pnames->rgNames);
|
|
pnames->rgNames = p;
|
|
pnames->cNames = i1;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
MIMEOLEAPI MimeOleCreateReceipt(IMimeMessage * pMsgSrc, PCX509CERT pCertToSign,
|
|
HWND hwndDlg, IMimeMessage ** ppMessage,
|
|
const CERT_ALT_NAME_INFO * pMyNames)
|
|
{
|
|
DWORD cb;
|
|
DWORD cLayers;
|
|
DWORD dwReceiptsFrom;
|
|
BOOL fSkipAddress = FALSE;
|
|
HRESULT hr;
|
|
DWORD i;
|
|
DWORD i1;
|
|
DWORD i2;
|
|
DWORD iAttr;
|
|
DWORD iLayer;
|
|
PCRYPT_ATTRIBUTES pattrs = NULL;
|
|
IMimeBody * pbody = NULL;
|
|
LPBYTE pbReceiptReq = NULL;
|
|
IMimeAddressTable * pmatbl = NULL;
|
|
IMimeBody * pmb = NULL;
|
|
IMimeMessage * pmm = NULL;
|
|
PSMIME_RECEIPT_REQUEST preq = NULL;
|
|
LPSTREAM pstm = NULL;
|
|
ReceiptNames receiptsTo = {0, NULL};
|
|
PROPVARIANT * rgpvAuthAttr = NULL;
|
|
PROPVARIANT var;
|
|
|
|
//
|
|
// Get the Layer Count
|
|
// Get the Authenticated Attributes
|
|
// Decode Receipt Request
|
|
// Set ReceiptsFrom from the request
|
|
// For Each layer
|
|
// is mlExpansion in this layer? No -- Skip to next layer
|
|
// Receipt for First Tier only? Yes - return S_FALSE
|
|
// Policy override on mlExpansion?
|
|
// None - return S_FALSE
|
|
// insteadOf - set ReceiptsFrom from mlExpansion History
|
|
// inAdditionTo - add to ReceiptsFrom
|
|
// Is my name in ReceiptsFrom list? No -- return S_FALSE
|
|
// Setup new IMimeMessage
|
|
// Attach receipt body
|
|
// Address from Receipt Request
|
|
// return S_OK
|
|
|
|
// Obtain the body of the message
|
|
|
|
hr = pMsgSrc->BindToObject(HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pbody);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
|
|
// Get the set of authenticated attributes on all layers of S/MIME in the
|
|
// message.
|
|
|
|
hr = pbody->GetOption(OID_SECURITY_SIGNATURE_COUNT, &var);
|
|
if (FAILED(hr)) {
|
|
goto GeneralFail;
|
|
}
|
|
cLayers = var.ulVal;
|
|
|
|
hr = pbody->GetOption(OID_SECURITY_AUTHATTR_RG, &var);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
rgpvAuthAttr = var.capropvar.pElems;
|
|
|
|
// Create a stream object to hold the receipt and put the receipt into the
|
|
// stream -- this supplies the body of the receipt message.
|
|
|
|
hr = MimeOleCreateVirtualStream(&pstm);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
|
|
hr = pbody->GetOption(OID_SECURITY_RECEIPT, &var);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
|
|
hr = pstm->Write(var.blob.pBlobData, var.blob.cbSize, NULL);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
|
|
//
|
|
// Walk through each layer of authenticated attributes processing the
|
|
// two relevant attributes.
|
|
//
|
|
|
|
for (iLayer=0; iLayer<cLayers; iLayer++) {
|
|
if (rgpvAuthAttr[iLayer].blob.cbSize == 0) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Decode the attributes at this layer of S/MIME
|
|
//
|
|
|
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_MSFT_ATTR_SEQUENCE,
|
|
rgpvAuthAttr[iLayer].blob.pBlobData,
|
|
rgpvAuthAttr[iLayer].blob.cbSize,
|
|
CRYPT_ENCODE_ALLOC_FLAG, &CryptDecodeAlloc,
|
|
&pattrs, &cb)) {
|
|
goto GeneralFail;
|
|
}
|
|
|
|
//
|
|
// Walk through each attribute looking for
|
|
// if innermost layer - the receipt request
|
|
// else - a Mail List expansion history
|
|
//
|
|
|
|
for (iAttr=0; iAttr<pattrs->cAttr; iAttr++) {
|
|
if (iLayer==0) {
|
|
if (strcmp(pattrs->rgAttr[iAttr].pszObjId,
|
|
szOID_SMIME_Receipt_Request) == 0) {
|
|
//
|
|
// Crack the contents of the receipt request
|
|
//
|
|
|
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_SMIME_Receipt_Request,
|
|
pattrs->rgAttr[iAttr].rgValue[0].pbData,
|
|
pattrs->rgAttr[iAttr].rgValue[0].cbData,
|
|
CRYPT_DECODE_ALLOC_FLAG,
|
|
&CryptDecodeAlloc, &preq, &cb)) {
|
|
goto GeneralFail;
|
|
}
|
|
|
|
//
|
|
// Initialize the ReceiptsTo list
|
|
//
|
|
|
|
if (preq->cReceiptsTo != 0) {
|
|
SetNames(&receiptsTo, preq->cReceiptsTo, preq->rgReceiptsTo);
|
|
}
|
|
|
|
// Who are receipts from?
|
|
|
|
dwReceiptsFrom = preq->ReceiptsFrom.AllOrFirstTier;
|
|
}
|
|
else if (strcmp(pattrs->rgAttr[iAttr].pszObjId,
|
|
szOID_RSA_messageDigest) == 0) {
|
|
;
|
|
}
|
|
}
|
|
else if ((iLayer != 0) && (strcmp(pattrs->rgAttr[iAttr].pszObjId,
|
|
szOID_SMIME_MLExpansion_History) == 0)) {
|
|
//
|
|
// If receipts are from first tier only and we see this attribute
|
|
// we are not first tier by definition.
|
|
//
|
|
|
|
if (dwReceiptsFrom == SMIME_RECEIPTS_FROM_FIRST_TIER) {
|
|
hr = S_FALSE;
|
|
goto CommonExit;
|
|
}
|
|
|
|
PSMIME_ML_EXPANSION_HISTORY pmlhist = NULL;
|
|
|
|
//
|
|
// Crack the attribute
|
|
//
|
|
|
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING,
|
|
szOID_SMIME_MLExpansion_History,
|
|
pattrs->rgAttr[iAttr].rgValue[0].pbData,
|
|
pattrs->rgAttr[iAttr].rgValue[0].cbData,
|
|
CRYPT_ENCODE_ALLOC_FLAG,
|
|
&CryptDecodeAlloc, &pmlhist, &cb)) {
|
|
goto GeneralFail;
|
|
}
|
|
|
|
PSMIME_MLDATA pMLData = &pmlhist->rgMLData[pmlhist->cMLData-1];
|
|
|
|
switch( pMLData->dwPolicy) {
|
|
// No receipt is to be returned
|
|
case SMIME_MLPOLICY_NONE:
|
|
hr = S_FALSE;
|
|
free(pmlhist);
|
|
goto CommonExit;
|
|
|
|
// Return receipt to a new list
|
|
case SMIME_MLPOLICY_INSTEAD_OF:
|
|
SetNames(&receiptsTo, pMLData->cNames, pMLData->rgNames);
|
|
break;
|
|
|
|
case SMIME_MLPOLICY_IN_ADDITION_TO:
|
|
MergeNames(&receiptsTo, pMLData->cNames, pMLData->rgNames);
|
|
break;
|
|
|
|
case SMIME_MLPOLICY_NO_CHANGE:
|
|
break;
|
|
|
|
default:
|
|
free(pmlhist);
|
|
goto GeneralFail;
|
|
}
|
|
|
|
free(pmlhist);
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(pattrs);
|
|
pattrs = NULL;
|
|
}
|
|
|
|
//
|
|
// Am I on the ReceiptsFrom List --
|
|
//
|
|
|
|
if (preq->ReceiptsFrom.cNames != 0) {
|
|
BOOL fFoundMe = FALSE;
|
|
|
|
for (i=0; !fFoundMe && (i<preq->ReceiptsFrom.cNames); i++) {
|
|
CERT_ALT_NAME_INFO * pname = NULL;
|
|
|
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
|
|
preq->ReceiptsFrom.rgNames[i].pbData,
|
|
preq->ReceiptsFrom.rgNames[i].cbData,
|
|
CRYPT_ENCODE_ALLOC_FLAG,
|
|
&CryptDecodeAlloc, &pname, &cb)) {
|
|
goto GeneralFail;
|
|
}
|
|
|
|
for (i1=0; i1<pname->cAltEntry; i1++) {
|
|
for (i2=0; i2<pMyNames->cAltEntry; i2++) {
|
|
if (pname->rgAltEntry[i1].dwAltNameChoice !=
|
|
pMyNames->rgAltEntry[i1].dwAltNameChoice) {
|
|
continue;
|
|
}
|
|
|
|
switch (pname->rgAltEntry[i1].dwAltNameChoice) {
|
|
case CERT_ALT_NAME_RFC822_NAME:
|
|
if (lstrcmpW(pname->rgAltEntry[i1].pwszRfc822Name,
|
|
pMyNames->rgAltEntry[i1].pwszRfc822Name) == 0) {
|
|
fFoundMe = TRUE;
|
|
goto FoundMe;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FoundMe:
|
|
free(pname);
|
|
}
|
|
|
|
if (!fFoundMe) {
|
|
hr = S_FALSE;
|
|
goto CommonExit;
|
|
}
|
|
}
|
|
|
|
hr = MimeOleCreateMessage(NULL, &pmm);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
|
|
hr = pmm->BindToObject(HBODY_ROOT, IID_IMimeBody, (LPVOID *) &pmb);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
|
|
hr = pmb->SetData(IET_BINARY, "OID", szOID_SMIME_ContentType_Receipt,
|
|
IID_IStream, pstm);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
|
|
//
|
|
// Address the receipt back to the receipients
|
|
//
|
|
|
|
hr = pmm->GetAddressTable(&pmatbl);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
|
|
for (i=0; i<receiptsTo.cNames; i++) {
|
|
CERT_ALT_NAME_INFO * pname = NULL;
|
|
|
|
if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
|
|
receiptsTo.rgNames[i].pbData,
|
|
receiptsTo.rgNames[i].cbData,
|
|
CRYPT_ENCODE_ALLOC_FLAG,
|
|
&CryptDecodeAlloc, &pname, &cb)) {
|
|
goto GeneralFail;
|
|
}
|
|
|
|
for (i1=0; i1<pname->cAltEntry; i1++) {
|
|
char cch;
|
|
char rgch[256];
|
|
|
|
if (pname->rgAltEntry[i1].dwAltNameChoice == CERT_ALT_NAME_RFC822_NAME) {
|
|
cch = WideCharToMultiByte(CP_ACP, 0,
|
|
pname->rgAltEntry[i1].pwszRfc822Name,
|
|
-1, rgch, sizeof(rgch), NULL, NULL);
|
|
if (cch > 0) {
|
|
hr = pmatbl->AppendRfc822(IAT_TO, IET_UNICODE,
|
|
rgch);
|
|
if (FAILED(hr)) {
|
|
goto CommonExit;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i1 == pname->cAltEntry) {
|
|
fSkipAddress = TRUE;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
{
|
|
LPSTREAM pstmTmp = NULL;
|
|
hr = MimeOleCreateVirtualStream(&pstmTmp);
|
|
pmm->Save(pstmTmp, TRUE);
|
|
pstmTmp->Release();
|
|
}
|
|
#endif // DEBUG
|
|
|
|
hr = S_OK;
|
|
*ppMessage = pmm;
|
|
pmm->AddRef();
|
|
|
|
CommonExit:
|
|
CoTaskMemFree(var.blob.pBlobData);
|
|
if (preq != NULL) free(preq);
|
|
if (pbReceiptReq != NULL) CoTaskMemFree(pbReceiptReq);
|
|
if (rgpvAuthAttr != NULL) CoTaskMemFree(rgpvAuthAttr);
|
|
if (pattrs != NULL) free(pattrs);
|
|
if (pstm != NULL) pstm->Release();
|
|
if (pmatbl != NULL) pmatbl->Release();
|
|
if (pmb != NULL) pmb->Release();
|
|
if (pmm != NULL) pmm->Release();
|
|
if (pbody != NULL) pbody->Release();
|
|
return hr;
|
|
|
|
GeneralFail:
|
|
hr = E_FAIL;
|
|
goto CommonExit;
|
|
}
|
|
#endif // SMIME_V3
|