#ifdef SMIME_V3 #include #include #include #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); irgNames = (CERT_NAME_BLOB *) malloc(cb); if (pnames->rgNames == NULL) { return E_OUTOFMEMORY; } pb = (LPBYTE) &pnames->rgNames[cNames]; for (i=0; irgNames[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; icNames; i++) { cb += pnames->rgNames[i].cbData; } for (i=0; icNames + cNames) * sizeof(CERT_NAME_BLOB)); if (p == NULL) { return E_OUTOFMEMORY; } pb = (LPBYTE) &p[pnames->cNames + cNames]; for (i=0, i1=0; icNames; 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; icNames; 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; iLayercAttr; 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 && (iReceiptsFrom.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; i1cAltEntry; i1++) { for (i2=0; i2cAltEntry; 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; icAltEntry; 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