Back to index

lightning-sunbird  0.9+nobinonly
nsCMSSecureMessage.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): Terry Hayes <thayes@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsMemory.h"
00039 #include "nsXPIDLString.h"
00040 #include "nsCOMPtr.h"
00041 #include "nsISupports.h"
00042 #include "nsIInterfaceRequestor.h"
00043 #include "nsCRT.h"
00044 
00045 #include "nsICMSSecureMessage.h"
00046 
00047 #include "nsCMSSecureMessage.h"
00048 #include "nsNSSCertificate.h"
00049 #include "nsNSSHelper.h"
00050 #include "nsNSSShutDown.h"
00051 
00052 #include <string.h>
00053 #include "plbase64.h"
00054 #include "cert.h"
00055 #include "cms.h"
00056 
00057 #include "nsIServiceManager.h"
00058 #include "nsIPrefService.h"
00059 #include "nsIPrefBranch.h"
00060 
00061 #include "prlog.h"
00062 #ifdef PR_LOGGING
00063 extern PRLogModuleInfo* gPIPNSSLog;
00064 #endif
00065 
00066 // Standard ISupports implementation
00067 // NOTE: Should these be the thread-safe versions?
00068 
00069 /*****
00070  * nsCMSSecureMessage
00071  *****/
00072 
00073 // Standard ISupports implementation
00074 NS_IMPL_ISUPPORTS1(nsCMSSecureMessage, nsICMSSecureMessage)
00075 
00076 // nsCMSSecureMessage constructor
00077 nsCMSSecureMessage::nsCMSSecureMessage()
00078 {
00079   // initialize superclass
00080 }
00081 
00082 // nsCMSMessage destructor
00083 nsCMSSecureMessage::~nsCMSSecureMessage()
00084 {
00085 }
00086 
00087 /* string getCertByPrefID (in string certID); */
00088 NS_IMETHODIMP nsCMSSecureMessage::
00089 GetCertByPrefID(const char *certID, char **_retval)
00090 {
00091   nsNSSShutDownPreventionLock locker;
00092   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::GetCertByPrefID\n"));
00093   nsresult rv = NS_OK;
00094   CERTCertificate *cert = 0;
00095   nsXPIDLCString nickname;
00096   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
00097 
00098   *_retval = 0;
00099 
00100   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
00101   if (NS_FAILED(rv)) {
00102     goto done;
00103   }
00104 
00105   rv = prefs->GetCharPref(certID,
00106                           getter_Copies(nickname));
00107   if (NS_FAILED(rv)) goto done;
00108 
00109   /* Find a good cert in the user's database */
00110   cert = CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), NS_CONST_CAST(char*, nickname.get()), 
00111            certUsageEmailRecipient, PR_TRUE, ctx);
00112 
00113   if (!cert) { 
00114     /* Success, but no value */
00115     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::GetCertByPrefID - can't find user cert\n"));
00116     goto done;
00117   } 
00118 
00119   /* Convert the DER to a BASE64 String */
00120   encode(cert->derCert.data, cert->derCert.len, _retval);
00121 
00122 done:
00123   if (cert) CERT_DestroyCertificate(cert);
00124   return rv;
00125 }
00126 
00127 
00128 // nsCMSSecureMessage::DecodeCert
00129 nsresult nsCMSSecureMessage::
00130 DecodeCert(const char *value, nsIX509Cert ** _retval)
00131 {
00132   nsNSSShutDownPreventionLock locker;
00133   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::DecodeCert\n"));
00134   nsresult rv = NS_OK;
00135   PRInt32 length;
00136   unsigned char *data = 0;
00137 
00138   *_retval = 0;
00139 
00140   if (!value) { return NS_ERROR_FAILURE; }
00141 
00142   rv = decode(value, &data, &length);
00143   if (NS_FAILED(rv)) {
00144     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::DecodeCert - can't decode cert\n"));
00145     return rv;
00146   }
00147 
00148   nsCOMPtr<nsIX509Cert> cert =  nsNSSCertificate::ConstructFromDER((char *)data, length);
00149 
00150   if (cert) {
00151     *_retval = cert;
00152     NS_ADDREF(*_retval);
00153   }
00154   else {
00155     rv = NS_ERROR_FAILURE;
00156   }
00157 
00158   nsCRT::free((char*)data);
00159   return rv;
00160 }
00161 
00162 // nsCMSSecureMessage::SendMessage
00163 nsresult nsCMSSecureMessage::
00164 SendMessage(const char *msg, const char *base64Cert, char ** _retval)
00165 {
00166   nsNSSShutDownPreventionLock locker;
00167   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage\n"));
00168   nsresult rv = NS_OK;
00169   CERTCertificate *cert = 0;
00170   NSSCMSMessage *cmsMsg = 0;
00171   unsigned char *certDER = 0;
00172   PRInt32 derLen;
00173   NSSCMSEnvelopedData *env;
00174   NSSCMSContentInfo *cinfo;
00175   NSSCMSRecipientInfo *rcpt;
00176   SECItem item;
00177   SECItem output;
00178   PLArenaPool *arena = PORT_NewArena(1024);
00179   SECStatus s;
00180 
00181   /* Step 0. Create a CMS Message */
00182   cmsMsg = NSS_CMSMessage_Create(NULL);
00183   if (!cmsMsg) {
00184     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create NSSCMSMessage\n"));
00185     rv = NS_ERROR_FAILURE;
00186     goto done;
00187   }
00188 
00189   /* Step 1.  Import the certificate into NSS */
00190   rv = decode(base64Cert, &certDER, &derLen);
00191   if (NS_FAILED(rv)) {
00192     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode / import cert into NSS\n"));
00193     goto done;
00194   }
00195 
00196   cert = CERT_DecodeCertFromPackage((char *)certDER, derLen);
00197   if (!cert) {
00198     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't decode cert from package\n"));
00199     rv = NS_ERROR_FAILURE;
00200     goto done;
00201   }
00202 
00203 #if 0
00204   cert->dbhandle = CERT_GetDefaultCertDB();  /* work-around */
00205 #endif
00206 
00207   /* Step 2.  Get a signature cert */
00208 
00209   /* Step 3. Build inner (signature) content */
00210 
00211   /* Step 4. Build outer (enveloped) content */
00212   env = NSS_CMSEnvelopedData_Create(cmsMsg, SEC_OID_DES_EDE3_CBC, 0);
00213   if (!env) {
00214     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create envelope data\n"));
00215     rv = NS_ERROR_FAILURE;
00216     goto done;
00217   }
00218 
00219   cinfo = NSS_CMSEnvelopedData_GetContentInfo(env);
00220   item.data = (unsigned char *)msg;
00221   item.len = strlen(msg);  /* XPCOM equiv?? */
00222   s = NSS_CMSContentInfo_SetContent_Data(cmsMsg, cinfo, 0, PR_FALSE);
00223   if (s != SECSuccess) {
00224     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content data\n"));
00225     rv = NS_ERROR_FAILURE;
00226     goto done;
00227   }
00228 
00229   rcpt = NSS_CMSRecipientInfo_Create(cmsMsg, cert);
00230   if (!rcpt) {
00231     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't create recipient info\n"));
00232     rv = NS_ERROR_FAILURE;
00233     goto done;
00234   }
00235 
00236   s = NSS_CMSEnvelopedData_AddRecipient(env, rcpt);
00237   if (s != SECSuccess) {
00238     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't add recipient\n"));
00239     rv = NS_ERROR_FAILURE;
00240     goto done;
00241   }
00242 
00243   /* Step 5. Add content to message */
00244   cinfo = NSS_CMSMessage_GetContentInfo(cmsMsg);
00245   s = NSS_CMSContentInfo_SetContent_EnvelopedData(cmsMsg, cinfo, env);
00246   if (s != SECSuccess) {
00247     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't set content enveloped data\n"));
00248     rv = NS_ERROR_FAILURE;
00249     goto done;
00250   }
00251   
00252   /* Step 6. Encode */
00253   NSSCMSEncoderContext *ecx;
00254 
00255   output.data = 0; output.len = 0;
00256   ecx = NSS_CMSEncoder_Start(cmsMsg, 0, 0, &output, arena,
00257             0, 0, 0, 0, 0, 0);
00258   if (!ecx) {
00259     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't start cms encoder\n"));
00260     rv = NS_ERROR_FAILURE;
00261     goto done;
00262   }
00263 
00264   s = NSS_CMSEncoder_Update(ecx, msg, strlen(msg));
00265   if (s != SECSuccess) {
00266     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't update encoder\n"));
00267     rv = NS_ERROR_FAILURE;
00268     goto done;
00269   }
00270 
00271   s = NSS_CMSEncoder_Finish(ecx);
00272   if (s != SECSuccess) {
00273     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::SendMessage - can't finish encoder\n"));
00274     rv = NS_ERROR_FAILURE;
00275     goto done;
00276   }
00277 
00278   /* Step 7. Base64 encode and return the result */
00279   rv = encode(output.data, output.len, _retval);
00280 
00281 done:
00282   if (certDER) nsCRT::free((char *)certDER);
00283   if (cert) CERT_DestroyCertificate(cert);
00284   if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg);
00285   if (arena) PORT_FreeArena(arena, PR_FALSE);  /* PR_FALSE? */
00286 
00287   return rv;
00288 }
00289 
00290 /*
00291  * nsCMSSecureMessage::ReceiveMessage
00292  */
00293 nsresult nsCMSSecureMessage::
00294 ReceiveMessage(const char *msg, char **_retval)
00295 {
00296   nsNSSShutDownPreventionLock locker;
00297   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage\n"));
00298   nsresult rv = NS_OK;
00299   NSSCMSDecoderContext *dcx;
00300   unsigned char *der = 0;
00301   PRInt32 derLen;
00302   NSSCMSMessage *cmsMsg = 0;
00303   SECItem *content;
00304   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
00305 
00306   /* Step 1. Decode the base64 wrapper */
00307   rv = decode(msg, &der, &derLen);
00308   if (NS_FAILED(rv)) {
00309     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't base64 decode\n"));
00310     goto done;
00311   }
00312 
00313   dcx = NSS_CMSDecoder_Start(0, 0, 0, /* pw */ 0, ctx, /* key */ 0, 0);
00314   if (!dcx) {
00315     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't start decoder\n"));
00316     rv = NS_ERROR_FAILURE;
00317     goto done;
00318   }
00319 
00320   (void)NSS_CMSDecoder_Update(dcx, (char *)der, derLen);
00321   cmsMsg = NSS_CMSDecoder_Finish(dcx);
00322   if (!cmsMsg) {
00323     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't finish decoder\n"));
00324     rv = NS_ERROR_FAILURE;
00325     /* Memory leak on dcx?? */
00326     goto done;
00327   }
00328 
00329   content = NSS_CMSMessage_GetContent(cmsMsg);
00330   if (!content) {
00331     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::ReceiveMessage - can't get content\n"));
00332     rv = NS_ERROR_FAILURE;
00333     goto done;
00334   }
00335 
00336   /* Copy the data */
00337   *_retval = (char*)malloc(content->len+1);
00338   memcpy(*_retval, content->data, content->len);
00339   (*_retval)[content->len] = 0;
00340 
00341 done:
00342   if (der) free(der);
00343   if (cmsMsg) NSS_CMSMessage_Destroy(cmsMsg);
00344 
00345   return rv;
00346 }
00347 
00348 nsresult nsCMSSecureMessage::
00349 encode(const unsigned char *data, PRInt32 dataLen, char **_retval)
00350 {
00351   nsresult rv = NS_OK;
00352 
00353   *_retval = PL_Base64Encode((const char *)data, dataLen, NULL);
00354   if (!*_retval) { rv = NS_ERROR_OUT_OF_MEMORY; goto loser; }
00355 
00356 loser:
00357   return rv;
00358 }
00359 
00360 nsresult nsCMSSecureMessage::
00361 decode(const char *data, unsigned char **result, PRInt32 * _retval)
00362 {
00363   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::decode\n"));
00364   nsresult rv = NS_OK;
00365   PRUint32 len = PL_strlen(data);
00366   int adjust = 0;
00367 
00368   /* Compute length adjustment */
00369   if (data[len-1] == '=') {
00370     adjust++;
00371     if (data[len-2] == '=') adjust++;
00372   }
00373 
00374   *result = (unsigned char *)PL_Base64Decode(data, len, NULL);
00375   if (!*result) {
00376     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSSecureMessage::decode - error decoding base64\n"));
00377     rv = NS_ERROR_ILLEGAL_VALUE;
00378     goto loser;
00379   }
00380 
00381   *_retval = (len*3)/4 - adjust;
00382 
00383 loser:
00384   return rv;
00385 }