From 77d80e392ff30f266ab4ffa264ff0afb3958ec04 Mon Sep 17 00:00:00 2001 From: "kaie%netscape.com" Date: Fri, 12 Apr 2002 04:26:40 +0000 Subject: [PATCH] b=119418 Fix handling of opaque signed S/Mime messages. Also fixes some other S/Mime issues, as described in the bug. r=ddrinan/ducarroz/darin sr=alecf git-svn-id: svn://10.0.0.236/trunk@118829 18797224-902f-48f8-a5cc-f745e15eee43 --- .../resources/content/msgReadSecurityInfo.js | 2 +- mozilla/mailnews/mime/src/mimecms.cpp | 123 +++++++++++---- mozilla/mailnews/mime/src/mimei.cpp | 3 +- mozilla/mailnews/mime/src/mimemcms.cpp | 2 +- mozilla/netwerk/mime/public/nsMimeTypes.h | 1 + mozilla/security/manager/ssl/src/nsCMS.cpp | 143 ++++++++++++------ mozilla/security/manager/ssl/src/nsCMS.h | 2 + 7 files changed, 195 insertions(+), 81 deletions(-) diff --git a/mozilla/mailnews/extensions/smime/resources/content/msgReadSecurityInfo.js b/mozilla/mailnews/extensions/smime/resources/content/msgReadSecurityInfo.js index 043923e38b0..7e32786d882 100644 --- a/mozilla/mailnews/extensions/smime/resources/content/msgReadSecurityInfo.js +++ b/mozilla/mailnews/extensions/smime/resources/content/msgReadSecurityInfo.js @@ -155,7 +155,7 @@ function onLoad() str = bundle.getString(sigInfo); } else if (sigInfo_clueless) { - str = bundle.getString("SIClueless") + " " + gSignatureStatus; + str = bundle.getString("SIClueless") + " (" + gSignatureStatus + ")"; } setText("signatureExplanation", str); diff --git a/mozilla/mailnews/mime/src/mimecms.cpp b/mozilla/mailnews/mime/src/mimecms.cpp index 2f234282e30..9191095eba9 100644 --- a/mozilla/mailnews/mime/src/mimecms.cpp +++ b/mozilla/mailnews/mime/src/mimecms.cpp @@ -213,7 +213,6 @@ MimeCMSHeadersAndCertsMatch(MimeObject *obj, nsXPIDLCString from_name; nsXPIDLCString sender_addr; nsXPIDLCString sender_name; - nsXPIDLCString cert_name; nsXPIDLCString cert_addr; PRBool match = PR_TRUE; @@ -221,11 +220,11 @@ MimeCMSHeadersAndCertsMatch(MimeObject *obj, */ if (content_info) { - content_info->GetSignerCommonName (getter_Copies(cert_name)); content_info->GetSignerEmailAddress (getter_Copies(cert_addr)); } - if (!cert_name && !cert_addr) goto DONE; + if (!cert_addr) + goto DONE; /* Find the headers of the MimeMessage which is the parent (or grandparent) of this object (remember, crypto objects nest.) */ @@ -450,42 +449,104 @@ MimeCMS_eof (void *crypto_closure, PRBool abort_p) PR_SetError(0, 0); rv = data->decoder_context->Finish(getter_AddRefs(data->content_info)); - nsCOMPtr recipientCert; - - /* Is the content info encrypted? */ - if (data->content_info) { - data->ci_is_encrypted = PR_TRUE; - data->content_info->GetEncryptionCert(getter_AddRefs(recipientCert)); - } - if (NS_FAILED(rv)) data->verify_error = PR_GetError(); - PRInt32 maxNestLevel = 0; - if (data->smimeHeaderSink) { - data->smimeHeaderSink->MaxWantedNesting(&maxNestLevel); + data->decoder_context = 0; - if (aNestLevel >= maxNestLevel) - { - PRInt32 status = nsICMSMessageErrors::GENERAL_ERROR; - - if (data->ci_is_encrypted - && !data->verify_error - && !data->decode_error - && NS_SUCCEEDED(rv)) - { - status = nsICMSMessageErrors::SUCCESS; + nsCOMPtr certOfInterest; + + if (!data->smimeHeaderSink) + return 0; + + PRInt32 maxNestLevel = 0; + data->smimeHeaderSink->MaxWantedNesting(&maxNestLevel); + + if (aNestLevel > maxNestLevel) + return 0; + + PRInt32 status = nsICMSMessageErrors::SUCCESS; + + if (data->verify_error + || data->decode_error + || NS_FAILED(rv)) + { + status = nsICMSMessageErrors::GENERAL_ERROR; + } + + if (!data->content_info) + { + status = nsICMSMessageErrors::GENERAL_ERROR; + + // Although a CMS message could be either encrypted or opaquely signed, + // what we see is most likely encrypted, because if it were + // signed only, we probably would have been able to decode it. + + data->ci_is_encrypted = PR_TRUE; + } + else + { + rv = data->content_info->ContentIsEncrypted(&data->ci_is_encrypted); + + if (NS_SUCCEEDED(rv) && data->ci_is_encrypted) { + data->content_info->GetEncryptionCert(getter_AddRefs(certOfInterest)); + } + else { + // Existing logic in mimei assumes, if !ci_is_encrypted, then it is signed. + // Make sure it indeed is signed. + + PRBool testIsSigned; + rv = data->content_info->ContentIsSigned(&testIsSigned); + + if (NS_FAILED(rv) || !testIsSigned) { + // Neither signed nor encrypted? + // We are unable to understand what we got, do not try to indicate S/Mime status. + return 0; } - - data->smimeHeaderSink->EncryptionStatus( - aNestLevel, - status, - recipientCert - ); + + rv = data->content_info->VerifySignature(); + + if (NS_FAILED(rv)) { + if (NS_ERROR_MODULE_SECURITY == NS_ERROR_GET_MODULE(rv)) { + status = NS_ERROR_GET_CODE(rv); + } + else if (NS_ERROR_NOT_IMPLEMENTED == rv) { + status = nsICMSMessageErrors::VERIFY_ERROR_PROCESSING; + } + } + else { + if (MimeCMSHeadersAndCertsMatch(data->self, + data->content_info, + &data->sender_addr)) + { + status = nsICMSMessageErrors::SUCCESS; + } + else + { + status = nsICMSMessageErrors::VERIFY_HEADER_MISMATCH; + } + } + + data->content_info->GetSignerCert(getter_AddRefs(certOfInterest)); } } - data->decoder_context = 0; + if (data->ci_is_encrypted) + { + data->smimeHeaderSink->EncryptionStatus( + aNestLevel, + status, + certOfInterest + ); + } + else + { + data->smimeHeaderSink->SignedStatus( + aNestLevel, + status, + certOfInterest + ); + } return 0; } diff --git a/mozilla/mailnews/mime/src/mimei.cpp b/mozilla/mailnews/mime/src/mimei.cpp index 9fcdec025cd..1a3433ede39 100644 --- a/mozilla/mailnews/mime/src/mimei.cpp +++ b/mozilla/mailnews/mime/src/mimei.cpp @@ -452,7 +452,8 @@ mime_find_class (const char *content_type, MimeHeaders *hdrs, } #ifdef ENABLE_SMIME - else if (!nsCRT::strcasecmp(content_type, APPLICATION_XPKCS7_MIME)) + else if (!nsCRT::strcasecmp(content_type, APPLICATION_XPKCS7_MIME) + || !nsCRT::strcasecmp(content_type, APPLICATION_PKCS7_MIME)) clazz = (MimeObjectClass *)&mimeEncryptedCMSClass; #endif /* A few types which occur in the real world and which we would otherwise diff --git a/mozilla/mailnews/mime/src/mimemcms.cpp b/mozilla/mailnews/mime/src/mimemcms.cpp index 0276f67a4ff..be1904cfdd2 100644 --- a/mozilla/mailnews/mime/src/mimemcms.cpp +++ b/mozilla/mailnews/mime/src/mimemcms.cpp @@ -509,7 +509,7 @@ MimeMultCMS_generate (void *crypto_closure) if (data->smimeHeaderSink) { data->smimeHeaderSink->MaxWantedNesting(&maxNestLevel); - if (aNestLevel >= maxNestLevel) + if (aNestLevel <= maxNestLevel) { data->smimeHeaderSink->SignedStatus(aNestLevel, signature_status, signerCert); } diff --git a/mozilla/netwerk/mime/public/nsMimeTypes.h b/mozilla/netwerk/mime/public/nsMimeTypes.h index 12917e5dd20..76fe49f4734 100644 --- a/mozilla/netwerk/mime/public/nsMimeTypes.h +++ b/mozilla/netwerk/mime/public/nsMimeTypes.h @@ -83,6 +83,7 @@ #define APPLICATION_X509_USER_CERT "application/x-x509-user-cert" #define APPLICATION_X509_CRL "application/x-pkcs7-crl" #define APPLICATION_XPKCS7_MIME "application/x-pkcs7-mime" +#define APPLICATION_PKCS7_MIME "application/pkcs7-mime" #define APPLICATION_XPKCS7_SIGNATURE "application/x-pkcs7-signature" #define APPLICATION_WWW_FORM_URLENCODED "application/x-www-form-urlencoded" #define APPLICATION_OLEOBJECT "application/oleobject" diff --git a/mozilla/security/manager/ssl/src/nsCMS.cpp b/mozilla/security/manager/ssl/src/nsCMS.cpp index e1b262cd198..d92729a975e 100644 --- a/mozilla/security/manager/ssl/src/nsCMS.cpp +++ b/mozilla/security/manager/ssl/src/nsCMS.cpp @@ -117,62 +117,99 @@ nsCMSMessage::~nsCMSMessage() NS_IMETHODIMP nsCMSMessage::VerifySignature() { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifySignature\n")); - return NS_ERROR_NOT_IMPLEMENTED; + return CommonVerifySignature(nsnull, 0); +} + +NSSCMSSignerInfo* nsCMSMessage::GetTopLevelSignerInfo() +{ + if (!m_cmsMsg) + return nsnull; + + if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) + return nsnull; + + NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0); + if (!cinfo) + return nsnull; + + NSSCMSSignedData *sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo); + if (!sigd) + return nsnull; + + PR_ASSERT(NSS_CMSSignedData_SignerInfoCount(sigd) > 0); + return NSS_CMSSignedData_GetSignerInfo(sigd, 0); } NS_IMETHODIMP nsCMSMessage::GetSignerEmailAddress(char * * aEmail) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerEmailAddress\n")); - return NS_ERROR_NOT_IMPLEMENTED; + NS_ENSURE_ARG(aEmail); + + NSSCMSSignerInfo *si = GetTopLevelSignerInfo(); + if (!si) + return NS_ERROR_FAILURE; + + *aEmail = NSS_CMSSignerInfo_GetSignerEmailAddress(si); + return NS_OK; } NS_IMETHODIMP nsCMSMessage::GetSignerCommonName(char ** aName) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerCommonName\n")); - return NS_ERROR_NOT_IMPLEMENTED; + NS_ENSURE_ARG(aName); + + NSSCMSSignerInfo *si = GetTopLevelSignerInfo(); + if (!si) + return NS_ERROR_FAILURE; + + *aName = NSS_CMSSignerInfo_GetSignerCommonName(si); + return NS_OK; } -NS_IMETHODIMP nsCMSMessage::ContentIsEncrypted(int *) +NS_IMETHODIMP nsCMSMessage::ContentIsEncrypted(PRBool *isEncrypted) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::ContentIsEncrypted\n")); - return NS_ERROR_NOT_IMPLEMENTED; + NS_ENSURE_ARG(isEncrypted); + + if (!m_cmsMsg) + return NS_ERROR_FAILURE; + + *isEncrypted = NSS_CMSMessage_IsEncrypted(m_cmsMsg); + + return NS_OK; } -NS_IMETHODIMP nsCMSMessage::ContentIsSigned(int *) +NS_IMETHODIMP nsCMSMessage::ContentIsSigned(PRBool *isSigned) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::ContentIsSigned\n")); - return NS_ERROR_NOT_IMPLEMENTED; + NS_ENSURE_ARG(isSigned); + + if (!m_cmsMsg) + return NS_ERROR_FAILURE; + + *isSigned = NSS_CMSMessage_IsSigned(m_cmsMsg); + + return NS_OK; } NS_IMETHODIMP nsCMSMessage::GetSignerCert(nsIX509Cert **scert) { - if (!m_cmsMsg) - return NS_ERROR_FAILURE; - - if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) - return NS_ERROR_FAILURE; - - NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0); - if (!cinfo) - return NS_ERROR_FAILURE; - - NSSCMSSignedData *sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo); - if (!sigd) - return NS_ERROR_FAILURE; - - PR_ASSERT(NSS_CMSSignedData_SignerInfoCount(sigd) > 0); - NSSCMSSignerInfo *si = NSS_CMSSignedData_GetSignerInfo(sigd, 0); + NSSCMSSignerInfo *si = GetTopLevelSignerInfo(); if (!si) return NS_ERROR_FAILURE; if (si->cert) { + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerCert got signer cert\n")); + *scert = new nsNSSCertificate(si->cert); if (*scert) { (*scert)->AddRef(); } } else { + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerCert no signer cert, do we have a cert list? %s\n", + (si->certList != nsnull ? "yes" : "no") )); + *scert = nsnull; } @@ -186,42 +223,54 @@ NS_IMETHODIMP nsCMSMessage::GetEncryptionCert(nsIX509Cert **ecert) NS_IMETHODIMP nsCMSMessage::VerifyDetachedSignature(unsigned char* aDigestData, PRUint32 aDigestDataLen) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature\n")); + if (!aDigestData || !aDigestDataLen) + return NS_ERROR_FAILURE; + + return CommonVerifySignature(aDigestData, aDigestDataLen); +} + +nsresult nsCMSMessage::CommonVerifySignature(unsigned char* aDigestData, PRUint32 aDigestDataLen) +{ + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature, content level count %d\n", NSS_CMSMessage_ContentLevelCount(m_cmsMsg))); NSSCMSContentInfo *cinfo = nsnull; NSSCMSSignedData *sigd = nsnull; NSSCMSSignerInfo *si; - SECItem digest; PRInt32 nsigners; nsresult rv = NS_ERROR_FAILURE; - digest.data = aDigestData; - digest.len = aDigestDataLen; - if (NSS_CMSMessage_IsSigned(m_cmsMsg) == PR_FALSE) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - not signed\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - not signed\n")); return NS_ERROR_CMS_VERIFY_NOT_SIGNED; } cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0); if (cinfo) { + // I don't like this hard cast. We should check in some way, that we really have this type. sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo); } if (!sigd) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - no content info\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - no content info\n")); rv = NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO; goto loser; } - if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - bad digest\n")); - rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST; - goto loser; + if (aDigestData && aDigestDataLen) + { + SECItem digest; + digest.data = aDigestData; + digest.len = aDigestDataLen; + + if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) { + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - bad digest\n")); + rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST; + goto loser; + } } // Import certs. Note that import failure is not a signature verification failure. // if (NSS_CMSSignedData_ImportCerts(sigd, CERT_GetDefaultCertDB(), certUsageEmailSigner, PR_TRUE) != SECSuccess) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - can not import certs\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - can not import certs\n")); } nsigners = NSS_CMSSignedData_SignerInfoCount(sigd); @@ -230,42 +279,42 @@ NS_IMETHODIMP nsCMSMessage::VerifyDetachedSignature(unsigned char* aDigestData, // We verify the first signer info, only // if (NSS_CMSSignedData_VerifySignerInfo(sigd, 0, CERT_GetDefaultCertDB(), certUsageEmailSigner) != SECSuccess) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - unable to verify signature\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - unable to verify signature\n")); if (NSSCMSVS_SigningCertNotFound == si->verificationStatus) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - signing cert not found\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - signing cert not found\n")); rv = NS_ERROR_CMS_VERIFY_NOCERT; } else if(NSSCMSVS_SigningCertNotTrusted == si->verificationStatus) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - signing cert not trusted\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - signing cert not trusted\n")); rv = NS_ERROR_CMS_VERIFY_UNTRUSTED; } else if(NSSCMSVS_Unverified == si->verificationStatus) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - can not verify\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - can not verify\n")); rv = NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED; } else if(NSSCMSVS_ProcessingError == si->verificationStatus) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - processing error\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - processing error\n")); rv = NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; } else if(NSSCMSVS_BadSignature == si->verificationStatus) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - bad signature\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - bad signature\n")); rv = NS_ERROR_CMS_VERIFY_BAD_SIGNATURE; } else if(NSSCMSVS_DigestMismatch == si->verificationStatus) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - digest mismatch\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - digest mismatch\n")); rv = NS_ERROR_CMS_VERIFY_DIGEST_MISMATCH; } else if(NSSCMSVS_SignatureAlgorithmUnknown == si->verificationStatus) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - algo unknown\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - algo unknown\n")); rv = NS_ERROR_CMS_VERIFY_UNKNOWN_ALGO; } else if(NSSCMSVS_SignatureAlgorithmUnsupported == si->verificationStatus) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - algo not supported\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - algo not supported\n")); rv = NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO; } else if(NSSCMSVS_MalformedSignature == si->verificationStatus) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - malformed signature\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - malformed signature\n")); rv = NS_ERROR_CMS_VERIFY_MALFORMED_SIGNATURE; } @@ -274,7 +323,7 @@ NS_IMETHODIMP nsCMSMessage::VerifyDetachedSignature(unsigned char* aDigestData, // Save the profile. Note that save import failure is not a signature verification failure. // if (NSS_SMIMESignerInfo_SaveSMIMEProfile(si) != SECSuccess) { - PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::VerifyDetachedSignature - unable to save smime profile\n")); + PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - unable to save smime profile\n")); } rv = NS_OK; diff --git a/mozilla/security/manager/ssl/src/nsCMS.h b/mozilla/security/manager/ssl/src/nsCMS.h index f116130cb53..4049106b4e2 100644 --- a/mozilla/security/manager/ssl/src/nsCMS.h +++ b/mozilla/security/manager/ssl/src/nsCMS.h @@ -79,6 +79,8 @@ public: NSSCMSMessage* getCMS() {return m_cmsMsg;}; private: NSSCMSMessage * m_cmsMsg; + NSSCMSSignerInfo* GetTopLevelSignerInfo(); + nsresult CommonVerifySignature(unsigned char* aDigestData, PRUint32 aDigestDataLen); };