Back to index

lightning-sunbird  0.9+nobinonly
nsMsgComposeSecure.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   David Drinan <ddrinan@netscape.com>
00025  *   Stephane Saux <ssaux@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include "nsMsgComposeSecure.h"
00042 #include "nspr.h"
00043 #include "nsCOMPtr.h"
00044 #include "nsIX509Cert.h"
00045 #include "nsISMimeCert.h"
00046 #include "nsIMimeConverter.h"
00047 #include "nsMimeStringResources.h"
00048 #include "nsMimeTypes.h"
00049 #include "nsIX509Cert.h"
00050 #include "nsIX509CertDB.h"
00051 #include "nsMsgBaseCID.h"
00052 #include "nsIMsgHeaderParser.h"
00053 #include "nsFileStream.h"
00054 #include "nsIServiceManager.h"
00055 #include "nsIMsgIdentity.h"
00056 #include "nsIMsgCompFields.h"
00057 #include "nsCRT.h"
00058 #include "nsReadableUtils.h"
00059 #include "nsArray.h"
00060 
00061 // String bundle for smime. Class static.
00062 nsCOMPtr<nsIStringBundle> nsMsgComposeSecure::mSMIMEBundle = nsnull;
00063 
00064 // XXX These strings should go in properties file XXX //
00065 #define MIME_MULTIPART_SIGNED_BLURB "This is a cryptographically signed message in MIME format."
00066 #define MIME_SMIME_ENCRYPTED_CONTENT_DESCRIPTION "S/MIME Encrypted Message"
00067 #define MIME_SMIME_SIGNATURE_CONTENT_DESCRIPTION "S/MIME Cryptographic Signature"
00068 
00069 #define MK_MIME_ERROR_WRITING_FILE -1
00070 
00071 #define SMIME_STRBUNDLE_URL "chrome://messenger/locale/am-smime.properties"
00072 
00073 static void mime_crypto_write_base64 (void *closure, const char *buf,
00074               unsigned long size);
00075 static nsresult PR_CALLBACK mime_encoder_output_fn(const char *buf, PRInt32 size, void *closure);
00076 static nsresult PR_CALLBACK mime_nested_encoder_output_fn (const char *buf, PRInt32 size, void *closure);
00077 static int make_multipart_signed_header_string(PRBool outer_p,
00078                   char **header_return,
00079                   char **boundary_return);
00080 static char *mime_make_separator(const char *prefix);
00081 
00082 // mscott --> FIX ME...for now cloning code from compose\nsMsgEncode.h/.cpp
00083 
00084 #include "prprf.h"
00085 #include "prmem.h"
00086 #include "nsCOMPtr.h"
00087 #include "nsMsgMimeCID.h"
00088 #include "nsIMimeConverter.h"
00089 
00090 MimeEncoderData *
00091 MIME_B64EncoderInit(nsresult (*PR_CALLBACK output_fn) (const char *buf, PRInt32 size, void *closure), void *closure) 
00092 {
00093   MimeEncoderData *returnEncoderData = nsnull;
00094   nsIMimeConverter *converter;
00095   nsresult res = CallCreateInstance(NS_MIME_CONVERTER_CONTRACTID, &converter);
00096   if (NS_SUCCEEDED(res) && nsnull != converter) 
00097   {
00098     res = converter->B64EncoderInit(output_fn, closure, &returnEncoderData);
00099     NS_RELEASE(converter);
00100   }
00101   return NS_SUCCEEDED(res) ? returnEncoderData : nsnull;
00102 }
00103 
00104 MimeEncoderData * 
00105 MIME_QPEncoderInit(nsresult (*PR_CALLBACK output_fn) (const char *buf, PRInt32 size, void *closure), void *closure) 
00106 {
00107   MimeEncoderData *returnEncoderData = nsnull;
00108   nsIMimeConverter *converter;
00109   nsresult res = CallCreateInstance(NS_MIME_CONVERTER_CONTRACTID, &converter);
00110   if (NS_SUCCEEDED(res) && nsnull != converter) 
00111   {
00112     res = converter->QPEncoderInit(output_fn, closure, &returnEncoderData);
00113     NS_RELEASE(converter);
00114   }
00115   return NS_SUCCEEDED(res) ? returnEncoderData : nsnull;
00116 }
00117 
00118 MimeEncoderData * 
00119 MIME_UUEncoderInit(char *filename, nsresult (*PR_CALLBACK output_fn) (const char *buf, PRInt32 size, void *closure), void *closure) 
00120 {
00121   MimeEncoderData *returnEncoderData = nsnull;
00122   nsIMimeConverter *converter;
00123   nsresult res = CallCreateInstance(NS_MIME_CONVERTER_CONTRACTID, &converter);
00124   if (NS_SUCCEEDED(res) && nsnull != converter) 
00125   {
00126     res = converter->UUEncoderInit(filename, output_fn, closure, &returnEncoderData);
00127     NS_RELEASE(converter);
00128   }
00129   return NS_SUCCEEDED(res) ? returnEncoderData : nsnull;
00130 }
00131 
00132 nsresult
00133 MIME_EncoderDestroy(MimeEncoderData *data, PRBool abort_p) 
00134 {
00135   //MimeEncoderData *returnEncoderData = nsnull;
00136   nsIMimeConverter *converter;
00137   nsresult res = CallCreateInstance(NS_MIME_CONVERTER_CONTRACTID, &converter);
00138   if (NS_SUCCEEDED(res) && nsnull != converter) 
00139   {
00140     res = converter->EncoderDestroy(data, abort_p);
00141     NS_RELEASE(converter);
00142   }
00143 
00144   return NS_SUCCEEDED(res) ? 0 : -1;
00145 }
00146 
00147 nsresult
00148 MIME_EncoderWrite(MimeEncoderData *data, const char *buffer, PRInt32 size) 
00149 {
00150   //  MimeEncoderData *returnEncoderData = nsnull;
00151   nsIMimeConverter *converter;
00152   PRInt32 written = 0;
00153   nsresult res = CallCreateInstance(NS_MIME_CONVERTER_CONTRACTID, &converter);
00154   if (NS_SUCCEEDED(res) && nsnull != converter) {
00155     res = converter->EncoderWrite(data, buffer, size, &written);
00156     NS_RELEASE(converter);
00157   }
00158   return NS_SUCCEEDED(res) ? 0 : -1;
00159 }
00160 
00161 static void
00162 GenerateGlobalRandomBytes(unsigned char *buf, PRInt32 len)
00163 {
00164   static PRBool    firstTime = PR_TRUE;
00165   
00166   if (firstTime)
00167   {
00168     // Seed the random-number generator with current time so that
00169     // the numbers will be different every time we run.
00170     PRInt32 aTime;
00171     LL_L2I(aTime, PR_Now());
00172     srand( (unsigned)aTime );
00173     firstTime = PR_FALSE;
00174   }
00175   
00176   for( PRInt32 i = 0; i < len; i++ )
00177     buf[i] = rand() % 10;
00178 }
00179    
00180 char 
00181 *mime_make_separator(const char *prefix)
00182 {
00183   unsigned char rand_buf[13]; 
00184   GenerateGlobalRandomBytes(rand_buf, 12);
00185 
00186   return PR_smprintf("------------%s"
00187            "%02X%02X%02X%02X"
00188            "%02X%02X%02X%02X"
00189            "%02X%02X%02X%02X",
00190            prefix,
00191            rand_buf[0], rand_buf[1], rand_buf[2], rand_buf[3],
00192            rand_buf[4], rand_buf[5], rand_buf[6], rand_buf[7],
00193            rand_buf[8], rand_buf[9], rand_buf[10], rand_buf[11]);
00194 }
00195 
00196 // end of copied code which needs fixed....
00197 
00199 // Implementation of nsMsgSMIMEComposeFields
00201 
00202 NS_IMPL_ISUPPORTS1(nsMsgSMIMEComposeFields, nsIMsgSMIMECompFields)
00203 
00204 nsMsgSMIMEComposeFields::nsMsgSMIMEComposeFields()
00205 :mSignMessage(PR_FALSE), mAlwaysEncryptMessage(PR_FALSE)
00206 {
00207 }
00208 
00209 nsMsgSMIMEComposeFields::~nsMsgSMIMEComposeFields()
00210 {
00211 }
00212 
00213 NS_IMETHODIMP nsMsgSMIMEComposeFields::SetSignMessage(PRBool value)
00214 {
00215   mSignMessage = value;
00216   return NS_OK;
00217 }
00218 
00219 NS_IMETHODIMP nsMsgSMIMEComposeFields::GetSignMessage(PRBool *_retval)
00220 {
00221   *_retval = mSignMessage;
00222   return NS_OK;
00223 }
00224 
00225 NS_IMETHODIMP nsMsgSMIMEComposeFields::SetRequireEncryptMessage(PRBool value)
00226 {
00227   mAlwaysEncryptMessage = value;
00228   return NS_OK;
00229 }
00230 
00231 NS_IMETHODIMP nsMsgSMIMEComposeFields::GetRequireEncryptMessage(PRBool *_retval)
00232 {
00233   *_retval = mAlwaysEncryptMessage;
00234   return NS_OK;
00235 }
00236 
00238 // Implementation of nsMsgComposeSecure
00240 
00241 NS_IMPL_ISUPPORTS1(nsMsgComposeSecure, nsIMsgComposeSecure)
00242 
00243 nsMsgComposeSecure::nsMsgComposeSecure()
00244 {
00245   /* member initializers and constructor code */
00246   mStream = 0;
00247   mDataHash = 0;
00248   mSigEncoderData = 0;
00249   mMultipartSignedBoundary  = 0;
00250   mSelfSigningCert = 0;
00251   mSelfEncryptionCert = 0;
00252   mCerts = 0;
00253   mEncryptionCinfo = 0;
00254   mEncryptionContext = 0;
00255   mCryptoEncoderData = 0;
00256   mBuffer = 0;
00257   mBufferedBytes = 0;
00258 }
00259 
00260 nsMsgComposeSecure::~nsMsgComposeSecure()
00261 {
00262   /* destructor code */
00263   if (mEncryptionContext) {
00264     if (mBufferedBytes) {
00265       mEncryptionContext->Update(mBuffer, mBufferedBytes);
00266       mBufferedBytes = 0;
00267     }
00268     mEncryptionContext->Finish();
00269   }
00270 
00271   if (mSigEncoderData) {
00272     MIME_EncoderDestroy (mSigEncoderData, PR_TRUE);
00273   }
00274   if (mCryptoEncoderData) {
00275     MIME_EncoderDestroy (mCryptoEncoderData, PR_TRUE);
00276   }
00277 
00278   delete [] mBuffer;
00279 
00280   PR_FREEIF(mMultipartSignedBoundary);
00281 }
00282 
00283 NS_IMETHODIMP nsMsgComposeSecure::RequiresCryptoEncapsulation(nsIMsgIdentity * aIdentity, nsIMsgCompFields * aCompFields, PRBool * aRequiresEncryptionWork)
00284 {
00285   NS_ENSURE_ARG_POINTER(aRequiresEncryptionWork);
00286 
00287   nsresult rv = NS_OK;
00288   *aRequiresEncryptionWork = PR_FALSE;
00289 
00290   PRBool alwaysEncryptMessages = PR_FALSE;
00291   PRBool signMessage = PR_FALSE;
00292   rv = ExtractEncryptionState(aIdentity, aCompFields, &signMessage, &alwaysEncryptMessages);
00293 
00294   if (alwaysEncryptMessages || signMessage)
00295     *aRequiresEncryptionWork = PR_TRUE;
00296 
00297   return NS_OK;
00298 }
00299 
00300 
00301 nsresult nsMsgComposeSecure::GetSMIMEBundleString(const PRUnichar *name,
00302                                                   PRUnichar **outString)
00303 {
00304   nsresult rv = NS_ERROR_FAILURE;
00305 
00306   *outString = nsnull;
00307 
00308   if ( ! mSMIMEBundle ) {
00309     InitializeSMIMEBundle();
00310     if ( ! mSMIMEBundle ) {
00311       return rv;
00312     }
00313   }
00314 
00315   if (name) {
00316     rv = mSMIMEBundle->GetStringFromName(name, outString);
00317     if (NS_SUCCEEDED(rv)) {
00318       rv = NS_OK;
00319     }
00320   }
00321 
00322   return rv;
00323 }
00324 
00325 
00326 nsresult
00327 nsMsgComposeSecure::
00328 SMIMEBundleFormatStringFromName(const PRUnichar *name,
00329                                 const PRUnichar **params,
00330                                 PRUint32 numParams,
00331                                 PRUnichar **outString)
00332 {
00333 
00334   nsresult rv = NS_ERROR_FAILURE;
00335 
00336   if ( ! mSMIMEBundle ) {
00337     InitializeSMIMEBundle();
00338     if ( ! mSMIMEBundle ) {
00339       return rv;
00340     }
00341   }
00342 
00343   if (name) {
00344     rv = mSMIMEBundle->FormatStringFromName(name, params, 
00345                                              numParams, outString);
00346   }
00347   return rv;
00348 }
00349 
00350 void nsMsgComposeSecure::InitializeSMIMEBundle()
00351 {
00352   nsresult rv;
00353 
00354   nsCOMPtr<nsIStringBundleService> bundleService(do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
00355 
00356   if ( NS_FAILED(rv) ) {
00357     return;
00358   }  
00359 
00360   bundleService->CreateBundle(SMIME_STRBUNDLE_URL,
00361                               getter_AddRefs(mSMIMEBundle));
00362 }
00363 
00364 void nsMsgComposeSecure::SetError(nsIMsgSendReport *sendReport, const PRUnichar *bundle_string)
00365 {
00366   if (!sendReport || !bundle_string)
00367     return;
00368 
00369   if (mErrorAlreadyReported)
00370     return;
00371 
00372   mErrorAlreadyReported = PR_TRUE;
00373   
00374   nsXPIDLString errorString;
00375   nsresult res;
00376 
00377   res = GetSMIMEBundleString(bundle_string,
00378                              getter_Copies(errorString));
00379 
00380   if (NS_SUCCEEDED(res) && !errorString.IsEmpty())
00381   {
00382     sendReport->SetMessage(nsIMsgSendReport::process_Current,
00383                            errorString.get(),
00384                            PR_TRUE);
00385   }
00386 }
00387 
00388 void nsMsgComposeSecure::SetErrorWithParam(nsIMsgSendReport *sendReport, const PRUnichar *bundle_string, const char *param)
00389 {
00390   if (!sendReport || !bundle_string || !param)
00391     return;
00392 
00393   if (mErrorAlreadyReported)
00394     return;
00395 
00396   mErrorAlreadyReported = PR_TRUE;
00397   
00398   nsXPIDLString errorString;
00399   nsresult res;
00400   const PRUnichar *params[1];
00401 
00402   NS_ConvertASCIItoUCS2 ucs2(param);
00403   params[0]= ucs2.get();
00404 
00405   res = SMIMEBundleFormatStringFromName(bundle_string,
00406                                         params,
00407                                         1,
00408                                         getter_Copies(errorString));
00409 
00410   if (NS_SUCCEEDED(res) && !errorString.IsEmpty())
00411   {
00412     sendReport->SetMessage(nsIMsgSendReport::process_Current,
00413                            errorString.get(),
00414                            PR_TRUE);
00415   }
00416 }
00417 
00418 nsresult nsMsgComposeSecure::ExtractEncryptionState(nsIMsgIdentity * aIdentity, nsIMsgCompFields * aComposeFields, PRBool * aSignMessage, PRBool * aEncrypt)
00419 {
00420   if (!aComposeFields && !aIdentity)
00421     return NS_ERROR_FAILURE; // kick out...invalid args....
00422 
00423   NS_ENSURE_ARG(aSignMessage);
00424   NS_ENSURE_ARG(aEncrypt);
00425 
00426   nsCOMPtr<nsISupports> securityInfo;
00427   if (aComposeFields)
00428     aComposeFields->GetSecurityInfo(getter_AddRefs(securityInfo));
00429 
00430   if (securityInfo) // if we were given security comp fields, use them.....
00431   {
00432     nsCOMPtr<nsIMsgSMIMECompFields> smimeCompFields = do_QueryInterface(securityInfo);
00433     if (smimeCompFields)
00434     {
00435       smimeCompFields->GetSignMessage(aSignMessage);
00436       smimeCompFields->GetRequireEncryptMessage(aEncrypt);
00437       return NS_OK;
00438     }
00439   }
00440 
00441   // get the default info from the identity....
00442   PRInt32 ep = 0;
00443   nsresult testrv = aIdentity->GetIntAttribute("encryptionpolicy", &ep);
00444   if (NS_FAILED(testrv)) {
00445     *aEncrypt = PR_FALSE;
00446   }
00447   else {
00448     *aEncrypt = (ep > 0);
00449   }
00450 
00451   testrv = aIdentity->GetBoolAttribute("sign_mail", aSignMessage);
00452   if (NS_FAILED(testrv))
00453   {
00454     *aSignMessage = PR_FALSE;
00455   }
00456   return NS_OK;
00457 }
00458 
00459 /* void beginCryptoEncapsulation (in nsOutputFileStream aStream, in boolean aEncrypt, in boolean aSign, in string aRecipeints, in boolean aIsDraft); */
00460 NS_IMETHODIMP nsMsgComposeSecure::BeginCryptoEncapsulation(nsOutputFileStream * aStream,
00461                                                            const char * aRecipients,
00462                                                            nsIMsgCompFields * aCompFields,
00463                                                            nsIMsgIdentity * aIdentity,
00464                                                            nsIMsgSendReport *sendReport,
00465                                                            PRBool aIsDraft)
00466 {
00467   mErrorAlreadyReported = PR_FALSE;
00468   nsresult rv = NS_OK;
00469 
00470   PRBool encryptMessages = PR_FALSE;
00471   PRBool signMessage = PR_FALSE;
00472   ExtractEncryptionState(aIdentity, aCompFields, &signMessage, &encryptMessages);
00473 
00474   if (!signMessage && !encryptMessages) return NS_ERROR_FAILURE;
00475 
00476   mStream = aStream;
00477   mIsDraft = aIsDraft;
00478 
00479   if (encryptMessages && signMessage)
00480     mCryptoState = mime_crypto_signed_encrypted;
00481   else if (encryptMessages)
00482     mCryptoState = mime_crypto_encrypted;
00483   else if (signMessage)
00484     mCryptoState = mime_crypto_clear_signed;
00485   else
00486     PR_ASSERT(0);
00487 
00488   aIdentity->GetUnicharAttribute("signing_cert_name", getter_Copies(mSigningCertName));
00489   aIdentity->GetUnicharAttribute("encryption_cert_name", getter_Copies(mEncryptionCertName));
00490 
00491   rv = MimeCryptoHackCerts(aRecipients, sendReport, encryptMessages, signMessage);
00492   if (NS_FAILED(rv)) {
00493     goto FAIL;
00494   }
00495 
00496   switch (mCryptoState)
00497   {
00498   case mime_crypto_clear_signed:
00499     rv = MimeInitMultipartSigned(PR_TRUE, sendReport);
00500     break;
00501   case mime_crypto_opaque_signed:
00502     PR_ASSERT(0);    /* #### no api for this yet */
00503     rv = NS_ERROR_NOT_IMPLEMENTED;
00504     break;
00505   case mime_crypto_signed_encrypted:
00506     rv = MimeInitEncryption(PR_TRUE, sendReport);
00507     break;
00508   case mime_crypto_encrypted:
00509     rv = MimeInitEncryption(PR_FALSE, sendReport);
00510     break;
00511   case mime_crypto_none:
00512     /* This can happen if mime_crypto_hack_certs() decided to turn off
00513      encryption (by asking the user.) */
00514     rv = 1;
00515     break;
00516   default:
00517     PR_ASSERT(0);
00518     break;
00519   }
00520 
00521 FAIL:
00522   return rv;
00523 }
00524 
00525 /* void finishCryptoEncapsulation (in boolean aAbort); */
00526 NS_IMETHODIMP nsMsgComposeSecure::FinishCryptoEncapsulation(PRBool aAbort, nsIMsgSendReport *sendReport)
00527 {
00528   nsresult rv = NS_OK;
00529 
00530   if (!aAbort) {
00531     switch (mCryptoState) {
00532     case mime_crypto_clear_signed:
00533       rv = MimeFinishMultipartSigned (PR_TRUE, sendReport);
00534       break;
00535     case mime_crypto_opaque_signed:
00536       PR_ASSERT(0);    /* #### no api for this yet */
00537       rv = NS_ERROR_FAILURE;
00538       break;
00539     case mime_crypto_signed_encrypted:
00540       rv = MimeFinishEncryption (PR_TRUE, sendReport);
00541       break;
00542     case mime_crypto_encrypted:
00543       rv = MimeFinishEncryption (PR_FALSE, sendReport);
00544       break;
00545     default:
00546       PR_ASSERT(0);
00547       rv = NS_ERROR_FAILURE;
00548       break;
00549     }
00550   }
00551   return rv;
00552 }
00553 
00554 nsresult nsMsgComposeSecure::MimeInitMultipartSigned(PRBool aOuter, nsIMsgSendReport *sendReport)
00555 {
00556   /* First, construct and write out the multipart/signed MIME header data.
00557    */
00558   nsresult rv = NS_OK;
00559   char *header = 0;
00560   PRInt32 L;
00561 
00562   rv = make_multipart_signed_header_string(aOuter, &header,
00563                     &mMultipartSignedBoundary);
00564   if (NS_FAILED(rv)) goto FAIL;
00565 
00566   L = strlen(header);
00567 
00568   if (aOuter){
00569     /* If this is the outer block, write it to the file. */
00570     if (PRInt32(mStream->write(header, L)) < L) {
00571       rv = MK_MIME_ERROR_WRITING_FILE;
00572     }
00573   } else {
00574     /* If this is an inner block, feed it through the crypto stream. */
00575     rv = MimeCryptoWriteBlock (header, L);
00576   }
00577 
00578   PR_Free(header);
00579   if (NS_FAILED(rv)) goto FAIL;
00580 
00581   /* Now initialize the crypto library, so that we can compute a hash
00582    on the object which we are signing.
00583    */
00584 
00585   mHashType = nsICryptoHash::SHA1;
00586 
00587   PR_SetError(0,0);
00588   mDataHash = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
00589   if (NS_FAILED(rv)) return 0;
00590 
00591   rv = mDataHash->Init(mHashType);
00592   if (NS_FAILED(rv)) {
00593     goto FAIL;
00594   }
00595 
00596   PR_SetError(0,0);
00597  FAIL:
00598   return rv;
00599 }
00600 
00601 nsresult nsMsgComposeSecure::MimeInitEncryption(PRBool aSign, nsIMsgSendReport *sendReport)
00602 {
00603   nsresult rv;
00604 
00605   /* First, construct and write out the opaque-crypto-blob MIME header data.
00606    */
00607 
00608   char *s =
00609   PR_smprintf("Content-Type: " APPLICATION_XPKCS7_MIME
00610           "; name=\"smime.p7m\"" CRLF
00611         "Content-Transfer-Encoding: " ENCODING_BASE64 CRLF
00612         "Content-Disposition: attachment"
00613           "; filename=\"smime.p7m\"" CRLF
00614         "Content-Description: %s" CRLF
00615         CRLF,
00616         MIME_SMIME_ENCRYPTED_CONTENT_DESCRIPTION);
00617   PRInt32 L;
00618   if (!s) return NS_ERROR_OUT_OF_MEMORY;
00619   L = strlen(s);
00620   if (PRInt32(mStream->write(s, L)) < L) {
00621     return NS_ERROR_FAILURE;
00622   }
00623   PR_Free(s);
00624   s = 0;
00625 
00626   /* Now initialize the crypto library, so that we can filter the object
00627    to be encrypted through it.
00628    */
00629 
00630   if (!mIsDraft) {
00631     PRUint32 numCerts;
00632     mCerts->GetLength(&numCerts);
00633     PR_ASSERT(numCerts > 0);
00634     if (numCerts == 0) return NS_ERROR_FAILURE;
00635   }
00636 
00637   /* Initialize the base64 encoder. */
00638   PR_ASSERT(!mCryptoEncoderData);
00639   mCryptoEncoderData = MIME_B64EncoderInit(mime_encoder_output_fn,
00640                           this);
00641   if (!mCryptoEncoderData) {
00642     return NS_ERROR_OUT_OF_MEMORY;
00643   }
00644 
00645   /* Initialize the encrypter (and add the sender's cert.) */
00646   PR_ASSERT(mSelfEncryptionCert);
00647   PR_SetError(0,0);
00648   mEncryptionCinfo = do_CreateInstance(NS_CMSMESSAGE_CONTRACTID, &rv);
00649   if (NS_FAILED(rv)) return rv;
00650   rv = mEncryptionCinfo->CreateEncrypted(mCerts);
00651   if (NS_FAILED(rv)) {
00652     SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotEncrypt").get());
00653     goto FAIL;
00654   }
00655 
00656   mEncryptionContext = do_CreateInstance(NS_CMSENCODER_CONTRACTID, &rv);
00657   if (NS_FAILED(rv)) return rv;
00658 
00659   if (!mBuffer) {
00660     mBuffer = new char[eBufferSize];
00661     if (!mBuffer)
00662       return NS_ERROR_OUT_OF_MEMORY;
00663   }
00664 
00665   mBufferedBytes = 0;
00666 
00667   rv = mEncryptionContext->Start(mEncryptionCinfo, mime_crypto_write_base64, mCryptoEncoderData);
00668   if (NS_FAILED(rv)) {
00669     SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotEncrypt").get());
00670     goto FAIL;
00671   }
00672 
00673   /* If we're signing, tack a multipart/signed header onto the front of
00674    the data to be encrypted, and initialize the sign-hashing code too.
00675    */
00676   if (aSign) {
00677     rv = MimeInitMultipartSigned(PR_FALSE, sendReport);
00678     if (NS_FAILED(rv)) goto FAIL;
00679   }
00680 
00681  FAIL:
00682   return rv;
00683 }
00684 
00685 nsresult nsMsgComposeSecure::MimeFinishMultipartSigned (PRBool aOuter, nsIMsgSendReport *sendReport)
00686 {
00687   int status;
00688   nsresult rv;
00689   nsCOMPtr<nsICMSMessage> cinfo = do_CreateInstance(NS_CMSMESSAGE_CONTRACTID, &rv);
00690   nsCOMPtr<nsICMSEncoder> encoder = do_CreateInstance(NS_CMSENCODER_CONTRACTID, &rv);
00691   char * header = nsnull;
00692 
00693   /* Compute the hash...
00694    */
00695 
00696   nsCAutoString hashString;
00697   mDataHash->Finish(PR_FALSE, hashString);
00698 
00699   mDataHash = 0;
00700 
00701   status = PR_GetError();
00702   if (status < 0) goto FAIL;
00703 
00704   /* Write out the headers for the signature.
00705    */
00706   PRInt32 L;
00707   header =
00708     PR_smprintf(CRLF
00709           "--%s" CRLF
00710           "Content-Type: " APPLICATION_XPKCS7_SIGNATURE
00711             "; name=\"smime.p7s\"" CRLF
00712           "Content-Transfer-Encoding: " ENCODING_BASE64 CRLF
00713           "Content-Disposition: attachment; "
00714             "filename=\"smime.p7s\"" CRLF
00715           "Content-Description: %s" CRLF
00716           CRLF,
00717           mMultipartSignedBoundary,
00718           MIME_SMIME_SIGNATURE_CONTENT_DESCRIPTION);
00719   if (!header) {
00720     rv = NS_ERROR_OUT_OF_MEMORY;
00721     goto FAIL;
00722   }
00723 
00724   L = strlen(header);
00725   if (aOuter) {
00726     /* If this is the outer block, write it to the file. */
00727     if (PRInt32(mStream->write(header, L)) < L) {
00728       rv = MK_MIME_ERROR_WRITING_FILE;
00729     } 
00730   } else {
00731     /* If this is an inner block, feed it through the crypto stream. */
00732     rv = MimeCryptoWriteBlock (header, L);
00733   }
00734 
00735   PR_Free(header);
00736 
00737   /* Create the signature...
00738    */
00739 
00740   PR_ASSERT(mHashType == nsICryptoHash::SHA1);
00741 
00742   PR_ASSERT (mSelfSigningCert);
00743   PR_SetError(0,0);
00744   
00745 
00746 
00747   rv = cinfo->CreateSigned(mSelfSigningCert, mSelfEncryptionCert, (unsigned char*)hashString.get(), hashString.Length());
00748   if (NS_FAILED(rv))  {
00749     SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotSign").get());
00750     goto FAIL;
00751   }
00752 
00753   /* Initialize the base64 encoder for the signature data.
00754    */
00755   PR_ASSERT(!mSigEncoderData);
00756   mSigEncoderData =
00757   MIME_B64EncoderInit((aOuter
00758             ? mime_encoder_output_fn
00759             : mime_nested_encoder_output_fn),
00760              this);
00761   if (!mSigEncoderData) {
00762     rv = NS_ERROR_OUT_OF_MEMORY;
00763     goto FAIL;
00764   }
00765 
00766   /* Write out the signature.
00767    */
00768   PR_SetError(0,0);
00769   rv = encoder->Start(cinfo, mime_crypto_write_base64, mSigEncoderData);
00770   if (NS_FAILED(rv)) {
00771     SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotSign").get());
00772     goto FAIL;
00773   }
00774 
00775   // We're not passing in any data, so no update needed.
00776   rv = encoder->Finish();
00777   if (NS_FAILED(rv)) {
00778     SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotSign").get());
00779     goto FAIL;
00780   }
00781 
00782   /* Shut down the sig's base64 encoder.
00783    */
00784   rv = MIME_EncoderDestroy(mSigEncoderData, PR_FALSE);
00785   mSigEncoderData = 0;
00786   if (NS_FAILED(rv)) {
00787     goto FAIL;
00788   }
00789 
00790   /* Now write out the terminating boundary.
00791    */
00792   {
00793   PRInt32 L;
00794   char *header = PR_smprintf(CRLF "--%s--" CRLF,
00795                  mMultipartSignedBoundary);
00796   PR_Free(mMultipartSignedBoundary);
00797   mMultipartSignedBoundary = 0;
00798 
00799   if (!header) {
00800     rv = NS_ERROR_OUT_OF_MEMORY;
00801     goto FAIL;
00802   }
00803   L = strlen(header);
00804   if (aOuter) {
00805     /* If this is the outer block, write it to the file. */
00806     if (PRInt32(mStream->write(header, L)) < L)
00807       rv = MK_MIME_ERROR_WRITING_FILE;
00808   } else {
00809     /* If this is an inner block, feed it through the crypto stream. */
00810     rv = MimeCryptoWriteBlock (header, L);
00811   }
00812   }
00813 
00814 FAIL:
00815   return rv;
00816 }
00817 
00818 
00819 /* Helper function for mime_finish_crypto_encapsulation() to close off
00820    an opaque crypto object (for encrypted or signed-and-encrypted messages.)
00821  */
00822 nsresult nsMsgComposeSecure::MimeFinishEncryption (PRBool aSign, nsIMsgSendReport *sendReport)
00823 {
00824   nsresult rv;
00825 
00826   /* If this object is both encrypted and signed, close off the
00827    signature first (since it's inside.) */
00828   if (aSign) {
00829     rv = MimeFinishMultipartSigned (PR_FALSE, sendReport);
00830     if (NS_FAILED(rv)) {
00831       goto FAIL;
00832     }
00833   }
00834 
00835   /* Close off the opaque encrypted blob.
00836    */
00837   PR_ASSERT(mEncryptionContext);
00838 
00839   if (mBufferedBytes) {
00840     rv = mEncryptionContext->Update(mBuffer, mBufferedBytes);
00841     mBufferedBytes = 0;
00842     if (NS_FAILED(rv)) {
00843       PR_ASSERT(PR_GetError() < 0);
00844       goto FAIL;
00845     }
00846   }
00847   
00848   rv = mEncryptionContext->Finish();
00849   if (NS_FAILED(rv)) {
00850     SetError(sendReport, NS_LITERAL_STRING("ErrorCanNotEncrypt").get());
00851     goto FAIL;
00852   }
00853 
00854   mEncryptionContext = 0;
00855 
00856   PR_ASSERT(mEncryptionCinfo);
00857   if (!mEncryptionCinfo) {
00858     rv = NS_ERROR_FAILURE;
00859   }
00860   if (mEncryptionCinfo) {
00861     mEncryptionCinfo = 0;
00862   }
00863 
00864   /* Shut down the base64 encoder. */
00865   rv = MIME_EncoderDestroy(mCryptoEncoderData, PR_FALSE);
00866   mCryptoEncoderData = 0;
00867 
00868   if (PRInt32(mStream->write(CRLF, 2)) < 2)
00869     rv = NS_ERROR_FAILURE;
00870 
00871  FAIL:
00872   return rv;
00873 }
00874 
00875 /* Used to figure out what certs should be used when encrypting this message.
00876  */
00877 nsresult nsMsgComposeSecure::MimeCryptoHackCerts(const char *aRecipients,
00878                                                  nsIMsgSendReport *sendReport,
00879                                                  PRBool aEncrypt,
00880                                                  PRBool aSign)
00881 {
00882   char *all_mailboxes = 0, *mailboxes = 0, *mailbox_list = 0;
00883   const char *mailbox = 0;
00884   PRUint32 count = 0;
00885   nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
00886   nsresult res;
00887   nsCOMPtr<nsIMsgHeaderParser> pHeader = do_GetService(NS_MAILNEWS_MIME_HEADER_PARSER_CONTRACTID, &res);
00888   NS_ENSURE_SUCCESS(res,res);
00889 
00890   res = NS_NewArray(getter_AddRefs(mCerts));
00891   if (NS_FAILED(res)) {
00892     return res;
00893   }
00894 
00895   PR_ASSERT(aEncrypt || aSign);
00896   certdb->FindEmailEncryptionCert(mEncryptionCertName, getter_AddRefs(mSelfEncryptionCert));
00897   certdb->FindEmailSigningCert(mSigningCertName, getter_AddRefs(mSelfSigningCert));
00898 
00899   // must have both the signing and encryption certs to sign
00900   if ((mSelfSigningCert == nsnull) && aSign) {
00901     SetError(sendReport, NS_LITERAL_STRING("NoSenderSigningCert").get());
00902     res = NS_ERROR_FAILURE;
00903     goto FAIL;
00904   }
00905 
00906   if ((mSelfEncryptionCert == nsnull) && aEncrypt) {
00907     SetError(sendReport, NS_LITERAL_STRING("NoSenderEncryptionCert").get());
00908     res = NS_ERROR_FAILURE;
00909     goto FAIL;
00910   }
00911 
00912   pHeader->ExtractHeaderAddressMailboxes(nsnull,aRecipients, &all_mailboxes);
00913   pHeader->RemoveDuplicateAddresses(nsnull, all_mailboxes, 0, PR_FALSE /*removeAliasesToMe*/, &mailboxes);
00914   if (all_mailboxes) {
00915     nsMemory::Free(all_mailboxes);
00916     all_mailboxes = nsnull;
00917   }
00918 
00919   if (mailboxes) {
00920     pHeader->ParseHeaderAddresses (nsnull, mailboxes, 0, &mailbox_list, &count);
00921     nsMemory::Free(mailboxes);
00922     mailboxes = nsnull;
00923   }
00924   if (count < 0) return count;
00925 
00926   if (aEncrypt && mSelfEncryptionCert) {
00927     // Make sure self's configured cert is prepared for being used
00928     // as an email recipient cert.
00929     
00930     nsCOMPtr<nsISMimeCert> sc = do_QueryInterface(mSelfEncryptionCert);
00931     if (sc) {
00932       sc->SaveSMimeProfile();
00933     }
00934   }
00935 
00936   /* If the message is to be encrypted, then get the recipient certs */
00937   if (aEncrypt) {
00938     mailbox = mailbox_list;
00939 
00940     PRBool already_added_self_cert = PR_FALSE;
00941 
00942     for (; count > 0; count--) {
00943       nsCString mailbox_lowercase;
00944       ToLowerCase(nsDependentCString(mailbox), mailbox_lowercase);
00945       nsCOMPtr<nsIX509Cert> cert;
00946       certdb->FindCertByEmailAddress(nsnull, mailbox_lowercase.get(), getter_AddRefs(cert));
00947       PRBool foundValidCert = PR_FALSE;
00948 
00949       if (cert) {
00950         PRUint32 verification_result;
00951 
00952         if (NS_SUCCEEDED(
00953             cert->VerifyForUsage(nsIX509Cert::CERT_USAGE_EmailRecipient, &verification_result))
00954             &&
00955             nsIX509Cert::VERIFIED_OK == verification_result)
00956         {
00957           foundValidCert = PR_TRUE;
00958         }
00959       }
00960       
00961       if (!foundValidCert) {
00962         // Failure to find a valid encryption cert is fatal.
00963         // here I assume that mailbox contains ascii rather than utf8.
00964         SetErrorWithParam(sendReport, NS_LITERAL_STRING("MissingRecipientEncryptionCert").get(), mailbox);
00965         res = NS_ERROR_FAILURE;
00966         goto FAIL;
00967       }
00968 
00969     /* #### see if recipient requests `signedData'.
00970      if (...) no_clearsigning_p = PR_TRUE;
00971      (This is the only reason we even bother looking up the certs
00972      of the recipients if we're sending a signed-but-not-encrypted
00973      message.)
00974      */
00975 
00976       PRBool isSame;
00977       if (NS_SUCCEEDED(cert->Equals(mSelfEncryptionCert, &isSame))
00978           && isSame) {
00979         already_added_self_cert = PR_TRUE;
00980       }
00981 
00982       mCerts->AppendElement(cert, PR_FALSE);
00983       // To understand this loop, especially the "+= strlen +1", look at the documentation
00984       // of ParseHeaderAddresses. Basically, it returns a list of zero terminated strings.
00985       mailbox += strlen(mailbox) + 1;
00986     }
00987     
00988     if (!already_added_self_cert) {
00989       mCerts->AppendElement(mSelfEncryptionCert, PR_FALSE);
00990     }
00991   }
00992 FAIL:
00993   if (mailbox_list) {
00994     nsMemory::Free(mailbox_list);
00995   }
00996   return res;
00997 }
00998 
00999 NS_IMETHODIMP nsMsgComposeSecure::MimeCryptoWriteBlock (const char *buf, PRInt32 size)
01000 {
01001   int status = 0;
01002   nsresult rv;
01003 
01004   /* If this is a From line, mangle it before signing it.  You just know
01005    that something somewhere is going to mangle it later, and that's
01006    going to cause the signature check to fail.
01007 
01008    (This assumes that, in the cases where From-mangling must happen,
01009    this function is called a line at a time.  That happens to be the
01010    case.)
01011   */
01012   if (size >= 5 && buf[0] == 'F' && !nsCRT::strncmp(buf, "From ", 5)) {
01013     char mangle[] = ">";
01014     status = MimeCryptoWriteBlock (mangle, 1);
01015     if (status < 0)
01016     return status;
01017   }
01018 
01019   /* If we're signing, or signing-and-encrypting, feed this data into
01020    the computation of the hash. */
01021   if (mDataHash) {
01022     PR_SetError(0,0);
01023     mDataHash->Update((const PRUint8*) buf, size);
01024          status = PR_GetError();
01025          if (status < 0) goto FAIL;
01026        }
01027 
01028   PR_SetError(0,0);
01029   if (mEncryptionContext) {
01030          /* If we're encrypting, or signing-and-encrypting, write this data
01031                by filtering it through the crypto library. */
01032 
01033     /* We want to create equally sized encryption strings */
01034     const char *inputBytesIterator = buf;
01035     PRUint32 inputBytesLeft = size;
01036 
01037     while (inputBytesLeft) {
01038       const PRUint32 spaceLeftInBuffer = eBufferSize - mBufferedBytes;
01039       const PRUint32 bytesToAppend = NS_MIN(inputBytesLeft, spaceLeftInBuffer);
01040 
01041       memcpy(mBuffer+mBufferedBytes, inputBytesIterator, bytesToAppend);
01042       mBufferedBytes += bytesToAppend;
01043       
01044       inputBytesIterator += bytesToAppend;
01045       inputBytesLeft -= bytesToAppend;
01046 
01047       if (eBufferSize == mBufferedBytes) {
01048         rv = mEncryptionContext->Update(mBuffer, mBufferedBytes);
01049         mBufferedBytes = 0;
01050         if (NS_FAILED(rv)) {
01051           status = PR_GetError();
01052           PR_ASSERT(status < 0);
01053           if (status >= 0) status = -1;
01054           goto FAIL;
01055         }
01056       }
01057     }
01058   } else {
01059          /* If we're not encrypting (presumably just signing) then write this
01060                data directly to the file. */
01061 
01062     if (PRInt32(mStream->write (buf, size)) < size) {
01063               return MK_MIME_ERROR_WRITING_FILE;
01064     }
01065        }
01066  FAIL:
01067   return status;
01068 }
01069 
01070 /* Returns a string consisting of a Content-Type header, and a boundary
01071    string, suitable for moving from the header block, down into the body
01072    of a multipart object.  The boundary itself is also returned (so that
01073    the caller knows what to write to close it off.)
01074  */
01075 static int
01076 make_multipart_signed_header_string(PRBool outer_p,
01077                                                                char **header_return,
01078                                                                char **boundary_return)
01079 {
01080   *header_return = 0;
01081   *boundary_return = mime_make_separator("ms");
01082   const char * crypto_multipart_blurb = nsnull;
01083 
01084   if (!*boundary_return)
01085        return NS_ERROR_OUT_OF_MEMORY;
01086 
01087   if (outer_p) {
01088          crypto_multipart_blurb = MIME_MULTIPART_SIGNED_BLURB;
01089   }
01090 
01091   *header_return =
01092        PR_smprintf("Content-Type: " MULTIPART_SIGNED "; "
01093                             "protocol=\"" APPLICATION_XPKCS7_SIGNATURE "\"; "
01094                             "micalg=" PARAM_MICALG_SHA1 "; "
01095                             "boundary=\"%s\"" CRLF
01096                             CRLF
01097                             "%s%s"
01098                             "--%s" CRLF,
01099 
01100                             *boundary_return,
01101                             (crypto_multipart_blurb ? crypto_multipart_blurb : ""),
01102                             (crypto_multipart_blurb ? CRLF CRLF : ""),
01103                             *boundary_return);
01104 
01105   if (!*header_return) {
01106          PR_Free(*boundary_return);
01107          *boundary_return = 0;
01108          return NS_ERROR_OUT_OF_MEMORY;
01109        }
01110 
01111   return 0;
01112 }
01113 
01114 /* Used as the output function of a SEC_PKCS7EncoderContext -- we feed
01115    plaintext into the crypto engine, and it calls this function with encrypted
01116    data; then this function writes a base64-encoded representation of that
01117    data to the file (by filtering it through the given MimeEncoderData object.)
01118 
01119    Also used as the output function of SEC_PKCS7Encode() -- but in that case,
01120    it's used to write the encoded representation of the signature.  The only
01121    difference is which MimeEncoderData object is used.
01122  */
01123 static void
01124 mime_crypto_write_base64 (void *closure, const char *buf,
01125                                             unsigned long size)
01126 {
01127   MimeEncoderData *data = (MimeEncoderData *) closure;
01128   int status = MIME_EncoderWrite (data, buf, size);
01129   PR_SetError(status < 0 ? status : 0, 0);
01130 }
01131 
01132 
01133 /* Used as the output function of MimeEncoderData -- when we have generated
01134    the signature for a multipart/signed object, this is used to write the
01135    base64-encoded representation of the signature to the file.
01136  */
01137 nsresult mime_encoder_output_fn(const char *buf, PRInt32 size, void *closure)
01138 {
01139   nsMsgComposeSecure *state = (nsMsgComposeSecure *) closure;
01140   nsOutputFileStream *stream = state->GetOutputStream();
01141   if (PRInt32(stream->write((char *) buf, size)) < size)
01142     return MK_MIME_ERROR_WRITING_FILE;
01143   else
01144     return 0;
01145 }
01146 
01147 /* Like mime_encoder_output_fn, except this is used for the case where we
01148    are both signing and encrypting -- the base64-encoded output of the
01149    signature should be fed into the crypto engine, rather than being written
01150    directly to the file.
01151  */
01152 static nsresult
01153 mime_nested_encoder_output_fn (const char *buf, PRInt32 size, void *closure)
01154 {
01155   nsMsgComposeSecure *state = (nsMsgComposeSecure *) closure;
01156   return state->MimeCryptoWriteBlock ((char *) buf, size);
01157 }