Back to index

lightning-sunbird  0.9+nobinonly
mimemcms.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corp..
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s): 
00023  *   Kai Engert <kengert@redhat.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsICMSMessage.h"
00040 #include "nsICMSMessageErrors.h"
00041 #include "nsICMSDecoder.h"
00042 #include "nsICryptoHash.h"
00043 #include "mimemcms.h"
00044 #include "mimecryp.h"
00045 #include "nsMimeTypes.h"
00046 #include "nspr.h"
00047 #include "nsMimeStringResources.h"
00048 #include "mimemsg.h"
00049 #include "mimemoz2.h"
00050 #include "nsIURI.h"
00051 #include "nsIMsgWindow.h"
00052 #include "nsIMsgMailNewsUrl.h"
00053 #include "nsIMimeMiscStatus.h"
00054 #include "nsIMsgSMIMEHeaderSink.h"
00055 #include "nsCOMPtr.h"
00056 #include "nsIX509Cert.h"
00057 
00058 #define MIME_SUPERCLASS mimeMultipartSignedClass
00059 MimeDefClass(MimeMultipartSignedCMS, MimeMultipartSignedCMSClass,
00060        mimeMultipartSignedCMSClass, &MIME_SUPERCLASS);
00061 
00062 static int MimeMultipartSignedCMS_initialize (MimeObject *);
00063 
00064 static void *MimeMultCMS_init (MimeObject *);
00065 static int MimeMultCMS_data_hash (char *, PRInt32, void *);
00066 static int MimeMultCMS_sig_hash  (char *, PRInt32, void *);
00067 static int MimeMultCMS_data_eof (void *, PRBool);
00068 static int MimeMultCMS_sig_eof  (void *, PRBool);
00069 static int MimeMultCMS_sig_init (void *, MimeObject *, MimeHeaders *);
00070 static char * MimeMultCMS_generate (void *);
00071 static void MimeMultCMS_free (void *);
00072 
00073 extern int SEC_ERROR_CERT_ADDR_MISMATCH;
00074 
00075 static int
00076 MimeMultipartSignedCMSClassInitialize(MimeMultipartSignedCMSClass *clazz)
00077 {
00078   MimeObjectClass          *oclass = (MimeObjectClass *)    clazz;
00079   MimeMultipartSignedClass *sclass = (MimeMultipartSignedClass *) clazz;
00080 
00081   oclass->initialize  = MimeMultipartSignedCMS_initialize;
00082 
00083   sclass->crypto_init           = MimeMultCMS_init;
00084   sclass->crypto_data_hash      = MimeMultCMS_data_hash;
00085   sclass->crypto_data_eof       = MimeMultCMS_data_eof;
00086   sclass->crypto_signature_init = MimeMultCMS_sig_init;
00087   sclass->crypto_signature_hash = MimeMultCMS_sig_hash;
00088   sclass->crypto_signature_eof  = MimeMultCMS_sig_eof;
00089   sclass->crypto_generate_html  = MimeMultCMS_generate;
00090   sclass->crypto_free           = MimeMultCMS_free;
00091 
00092   PR_ASSERT(!oclass->class_initialized);
00093   return 0;
00094 }
00095 
00096 static int
00097 MimeMultipartSignedCMS_initialize (MimeObject *object)
00098 {
00099   return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
00100 }
00101 
00102 
00103 typedef struct MimeMultCMSdata
00104 {
00105   PRInt16 hash_type;
00106   nsCOMPtr<nsICryptoHash> data_hash_context;
00107   nsCOMPtr<nsICMSDecoder> sig_decoder_context;
00108   nsCOMPtr<nsICMSMessage> content_info;
00109   char *sender_addr;
00110   PRBool decoding_failed;
00111   unsigned char* item_data;
00112   PRUint32 item_len;
00113   MimeObject *self;
00114   PRBool parent_is_encrypted_p;
00115   PRBool parent_holds_stamp_p;
00116   nsCOMPtr<nsIMsgSMIMEHeaderSink> smimeHeaderSink;
00117   
00118   MimeMultCMSdata()
00119   :hash_type(0),
00120   sender_addr(nsnull),
00121   decoding_failed(PR_FALSE),
00122   item_data(nsnull),
00123   self(nsnull),
00124   parent_is_encrypted_p(PR_FALSE),
00125   parent_holds_stamp_p(PR_FALSE)
00126   {
00127   }
00128   
00129   ~MimeMultCMSdata()
00130   {
00131     PR_FREEIF(sender_addr);
00132 
00133     // Do a graceful shutdown of the nsICMSDecoder and release the nsICMSMessage //
00134     if (sig_decoder_context)
00135     {
00136       nsCOMPtr<nsICMSMessage> cinfo;
00137       sig_decoder_context->Finish(getter_AddRefs(cinfo));
00138     }
00139 
00140     delete [] item_data;
00141   }
00142 } MimeMultCMSdata;
00143 
00144 /* #### MimeEncryptedCMS and MimeMultipartSignedCMS have a sleazy,
00145         incestuous, dysfunctional relationship. */
00146 extern PRBool MimeEncryptedCMS_encrypted_p (MimeObject *obj);
00147 extern void MimeCMSGetFromSender(MimeObject *obj,
00148                                  nsXPIDLCString &from_addr,
00149                                  nsXPIDLCString &from_name,
00150                                  nsXPIDLCString &sender_addr,
00151                                  nsXPIDLCString &sender_name);
00152 extern PRBool MimeCMSHeadersAndCertsMatch(MimeObject *obj,
00153                                           nsICMSMessage *,
00154                                           PRBool *signing_cert_without_email_address);
00155 extern void MimeCMSRequestAsyncSignatureVerification(nsICMSMessage *aCMSMsg,
00156                                                      const char *aFromAddr, const char *aFromName,
00157                                                      const char *aSenderAddr, const char *aSenderName,
00158                                                      nsIMsgSMIMEHeaderSink *aHeaderSink, PRInt32 aMimeNestingLevel,
00159                                                      unsigned char* item_data, PRUint32 item_len);
00160 extern char *MimeCMS_MakeSAURL(MimeObject *obj);
00161 extern char *IMAP_CreateReloadAllPartsUrl(const char *url);
00162 extern int MIMEGetRelativeCryptoNestLevel(MimeObject *obj);
00163 
00164 static void *
00165 MimeMultCMS_init (MimeObject *obj)
00166 {
00167   MimeHeaders *hdrs = obj->headers;
00168   MimeMultCMSdata *data = 0;
00169   char *ct, *micalg;
00170   PRInt16 hash_type;
00171   nsresult rv;
00172 
00173   ct = MimeHeaders_get (hdrs, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
00174   if (!ct) return 0; /* #### bogus message?  out of memory? */
00175   micalg = MimeHeaders_get_parameter (ct, PARAM_MICALG, NULL, NULL);
00176   PR_Free(ct);
00177   ct = 0;
00178   if (!micalg) return 0; /* #### bogus message?  out of memory? */
00179 
00180   if (!nsCRT::strcasecmp(micalg, PARAM_MICALG_MD5) ||
00181       !nsCRT::strcasecmp(micalg, PARAM_MICALG_MD5_2))
00182     hash_type = nsICryptoHash::MD5;
00183   else if (!nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1) ||
00184        !nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1_2) ||
00185        !nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1_3) ||
00186        !nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1_4) ||
00187        !nsCRT::strcasecmp(micalg, PARAM_MICALG_SHA1_5))
00188     hash_type = nsICryptoHash::SHA1;
00189   else if (!nsCRT::strcasecmp(micalg, PARAM_MICALG_MD2))
00190     hash_type = nsICryptoHash::MD2;
00191   else
00192     hash_type = -1;
00193 
00194   PR_Free(micalg);
00195   micalg = 0;
00196 
00197   if (hash_type == -1) return 0; /* #### bogus message? */
00198 
00199   data = new MimeMultCMSdata;
00200   if (!data)
00201     return 0;
00202 
00203   data->self = obj;
00204   data->hash_type = hash_type;
00205 
00206   data->data_hash_context = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
00207   if (NS_FAILED(rv)) return 0;
00208 
00209   rv = data->data_hash_context->Init(data->hash_type);
00210   if (NS_FAILED(rv)) return 0;
00211 
00212   PR_SetError(0,0);
00213 
00214   data->parent_holds_stamp_p =
00215   (obj->parent && mime_crypto_stamped_p(obj->parent));
00216 
00217   data->parent_is_encrypted_p =
00218   (obj->parent && MimeEncryptedCMS_encrypted_p (obj->parent));
00219 
00220   /* If the parent of this object is a crypto-blob, then it's the grandparent
00221    who would have written out the headers and prepared for a stamp...
00222    (This s##t s$%#s.)
00223    */
00224   if (data->parent_is_encrypted_p &&
00225     !data->parent_holds_stamp_p &&
00226     obj->parent && obj->parent->parent)
00227   data->parent_holds_stamp_p =
00228     mime_crypto_stamped_p (obj->parent->parent);
00229 
00230   mime_stream_data *msd = (mime_stream_data *) (data->self->options->stream_closure);
00231   if (msd)
00232   {
00233     nsIChannel *channel = msd->channel;  // note the lack of ref counting...
00234     if (channel)
00235     {
00236       nsCOMPtr<nsIURI> uri;
00237       nsCOMPtr<nsIMsgWindow> msgWindow;
00238       nsCOMPtr<nsIMsgHeaderSink> headerSink;
00239       nsCOMPtr<nsIMsgMailNewsUrl> msgurl;
00240       nsCOMPtr<nsISupports> securityInfo;
00241       channel->GetURI(getter_AddRefs(uri));
00242       if (uri)
00243       {
00244         nsCAutoString urlSpec;
00245         rv = uri->GetSpec(urlSpec);
00246 
00247         // We only want to update the UI if the current mime transaction
00248         // is intended for display.
00249         // If the current transaction is intended for background processing,
00250         // we can learn that by looking at the additional header=filter
00251         // string contained in the URI.
00252         //
00253         // If we find something, we do not set smimeHeaderSink,
00254         // which will prevent us from giving UI feedback.
00255         //
00256         // If we do not find header=filter, we assume the result of the
00257         // processing will be shown in the UI.
00258         
00259         if (!strstr(urlSpec.get(), "?header=filter") &&
00260             !strstr(urlSpec.get(), "&header=filter")&&
00261             !strstr(urlSpec.get(), "?header=attach") &&
00262             !strstr(urlSpec.get(), "&header=attach"))
00263         {
00264           msgurl = do_QueryInterface(uri);
00265           if (msgurl)
00266             msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
00267           if (msgWindow)
00268             msgWindow->GetMsgHeaderSink(getter_AddRefs(headerSink));
00269           if (headerSink)
00270             headerSink->GetSecurityInfo(getter_AddRefs(securityInfo));
00271           if (securityInfo)
00272             data->smimeHeaderSink = do_QueryInterface(securityInfo);
00273          }
00274        }
00275     } // if channel
00276   } // if msd
00277 
00278   return data;
00279 }
00280 
00281 static int
00282 MimeMultCMS_data_hash (char *buf, PRInt32 size, void *crypto_closure)
00283 {
00284   MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
00285   if (!data || !data->data_hash_context) {
00286     return -1;
00287   }
00288 
00289   PR_SetError(0, 0);
00290   nsresult rv = data->data_hash_context->Update((unsigned char *) buf, size);
00291   data->decoding_failed = NS_FAILED(rv);
00292 
00293   return 0;
00294 }
00295 
00296 static int
00297 MimeMultCMS_data_eof (void *crypto_closure, PRBool abort_p)
00298 {
00299   MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
00300   if (!data || !data->data_hash_context) {
00301     return -1;
00302   }
00303 
00304   nsCAutoString hashString;
00305   data->data_hash_context->Finish(PR_FALSE, hashString);
00306   PR_SetError(0, 0);
00307   
00308   data->item_len  = hashString.Length();
00309   data->item_data = new unsigned char[data->item_len];
00310   if (!data->item_data) return MIME_OUT_OF_MEMORY;
00311   
00312   memcpy(data->item_data, hashString.get(), data->item_len);
00313 
00314   // Release our reference to nsICryptoHash //
00315   data->data_hash_context = 0;
00316 
00317   /* At this point, data->item.data contains a digest for the first part.
00318    When we process the signature, the security library will compare this
00319    digest to what's in the signature object. */
00320 
00321   return 0;
00322 }
00323 
00324 
00325 static int
00326 MimeMultCMS_sig_init (void *crypto_closure,
00327             MimeObject *multipart_object,
00328             MimeHeaders *signature_hdrs)
00329 {
00330   MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
00331   char *ct;
00332   int status = 0;
00333   nsresult rv;
00334 
00335   if (!signature_hdrs) {
00336     return -1;
00337   }
00338 
00339   ct = MimeHeaders_get (signature_hdrs, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE);
00340 
00341   /* Verify that the signature object is of the right type. */
00342   if (!ct || /* is not a signature type */
00343              (nsCRT::strcasecmp(ct, APPLICATION_XPKCS7_SIGNATURE) != 0
00344               && nsCRT::strcasecmp(ct, APPLICATION_PKCS7_SIGNATURE) != 0)) {
00345     status = -1; /* #### error msg about bogus message */
00346   }
00347   PR_FREEIF(ct);
00348   if (status < 0) return status;
00349 
00350   data->sig_decoder_context = do_CreateInstance(NS_CMSDECODER_CONTRACTID, &rv);
00351   if (NS_FAILED(rv)) return 0;
00352 
00353   rv = data->sig_decoder_context->Start(nsnull, nsnull);
00354   if (NS_FAILED(rv)) {
00355     status = PR_GetError();
00356     if (status >= 0) status = -1;
00357   }
00358   return status;
00359 }
00360 
00361 
00362 static int
00363 MimeMultCMS_sig_hash (char *buf, PRInt32 size, void *crypto_closure)
00364 {
00365   MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
00366   nsresult rv;
00367 
00368   if (!data || !data->sig_decoder_context) {
00369     return -1;
00370   }
00371 
00372   rv = data->sig_decoder_context->Update(buf, size);
00373   data->decoding_failed = NS_FAILED(rv);
00374 
00375   return 0;
00376 }
00377 
00378 static int
00379 MimeMultCMS_sig_eof (void *crypto_closure, PRBool abort_p)
00380 {
00381   MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
00382 
00383   if (!data) {
00384     return -1;
00385   }
00386 
00387   /* Hand an EOF to the crypto library.
00388 
00389    We save away the value returned and will use it later to emit a
00390    blurb about whether the signature validation was cool.
00391    */
00392 
00393   if (data->sig_decoder_context) {
00394     data->sig_decoder_context->Finish(getter_AddRefs(data->content_info));
00395 
00396     // Release our reference to nsICMSDecoder //
00397     data->sig_decoder_context = 0;
00398   }
00399 
00400   return 0;
00401 }
00402 
00403 static void
00404 MimeMultCMS_free (void *crypto_closure)
00405 {
00406   MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
00407   if (!data) return;
00408 
00409   delete data;
00410 }
00411 
00412 static char *
00413 MimeMultCMS_generate (void *crypto_closure)
00414 {
00415   MimeMultCMSdata *data = (MimeMultCMSdata *) crypto_closure;
00416   PRBool encrypted_p;
00417   if (!data) return 0;
00418   encrypted_p = data->parent_is_encrypted_p;
00419   nsCOMPtr<nsIX509Cert> signerCert;
00420 
00421   int aRelativeNestLevel = MIMEGetRelativeCryptoNestLevel(data->self);
00422 
00423   if (aRelativeNestLevel < 0)
00424     return nsnull;
00425 
00426   PRInt32 maxNestLevel = 0;
00427   if (data->smimeHeaderSink && aRelativeNestLevel >= 0)
00428   {
00429     data->smimeHeaderSink->MaxWantedNesting(&maxNestLevel);
00430 
00431     if (aRelativeNestLevel > maxNestLevel)
00432       return nsnull;
00433   }
00434 
00435   if (data->self->options->missing_parts)
00436   {
00437     // We were not given all parts of the message.
00438     // We are therefore unable to verify correctness of the signature.
00439     
00440     if (data->smimeHeaderSink)
00441       data->smimeHeaderSink->SignedStatus(aRelativeNestLevel, 
00442                                           nsICMSMessageErrors::VERIFY_NOT_YET_ATTEMPTED, 
00443                                           nsnull);
00444     return nsnull;
00445   }
00446 
00447   if (!data->content_info)
00448   {
00449     /* No content_info at all -- since we're inside a multipart/signed,
00450      that means that we've either gotten a message that was truncated
00451      before the signature part, or we ran out of memory, or something
00452      awful has happened.
00453      */
00454      return nsnull;
00455   }
00456   
00457   nsXPIDLCString from_addr;
00458   nsXPIDLCString from_name;
00459   nsXPIDLCString sender_addr;
00460   nsXPIDLCString sender_name;
00461   
00462   MimeCMSGetFromSender(data->self, 
00463                        from_addr, from_name,
00464                        sender_addr, sender_name);
00465 
00466   MimeCMSRequestAsyncSignatureVerification(data->content_info, 
00467                                            from_addr, from_name,
00468                                            sender_addr, sender_name,
00469                                            data->smimeHeaderSink, aRelativeNestLevel, 
00470                                            data->item_data, data->item_len);
00471 
00472   if (data->content_info)
00473   {
00474 #if 0 // XXX Fix this. What do we do here? //
00475     if (SEC_CMSContainsCertsOrCrls(data->content_info))
00476     {
00477       /* #### call libsec telling it to import the certs */
00478     }
00479 #endif
00480   }
00481 
00482   return nsnull;
00483 }