Back to index

lightning-sunbird  0.9+nobinonly
nsCRLManager.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #include "nsCRLManager.h"
00038 #include "nsCRLInfo.h"
00039 
00040 #include "nsCOMPtr.h"
00041 #include "nsIDateTimeFormat.h"
00042 #include "nsDateTimeFormatCID.h"
00043 #include "nsComponentManagerUtils.h"
00044 #include "nsReadableUtils.h"
00045 #include "nsNSSComponent.h"
00046 #include "nsIWindowWatcher.h"
00047 #include "nsCOMPtr.h"
00048 #include "nsIPrompt.h"
00049 #include "nsICertificateDialogs.h"
00050 #include "nsArray.h"
00051 #include "nsIPrefService.h"
00052 #include "nsIPrefBranch.h"
00053 #include "nsNSSShutDown.h"
00054 
00055 #include "nsNSSCertHeader.h"
00056 
00057 #include "nspr.h"
00058 extern "C" {
00059 #include "pk11func.h"
00060 #include "certdb.h"
00061 #include "cert.h"
00062 #include "secerr.h"
00063 #include "nssb64.h"
00064 #include "secasn1.h"
00065 #include "secder.h"
00066 }
00067 #include "ssl.h"
00068 #include "ocsp.h"
00069 #include "plbase64.h"
00070 
00071 static NS_DEFINE_CID(kDateTimeFormatCID, NS_DATETIMEFORMAT_CID);
00072 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
00073 
00074 NS_IMPL_ISUPPORTS1(nsCRLManager, nsICRLManager)
00075 
00076 nsCRLManager::nsCRLManager()
00077 {
00078 }
00079 
00080 nsCRLManager::~nsCRLManager()
00081 {
00082 }
00083 
00084 NS_IMETHODIMP 
00085 nsCRLManager::ImportCrl (PRUint8 *aData, PRUint32 aLength, nsIURI * aURI, PRUint32 aType, PRBool doSilentDonwload, const PRUnichar* crlKey)
00086 {
00087   nsNSSShutDownPreventionLock locker;
00088   nsresult rv;
00089   PRArenaPool *arena = NULL;
00090   CERTCertificate *caCert;
00091   SECItem derName = { siBuffer, NULL, 0 };
00092   SECItem derCrl;
00093   CERTSignedData sd;
00094   SECStatus sec_rv;
00095   CERTSignedCrl *crl;
00096   nsCAutoString url;
00097   nsCOMPtr<nsICRLInfo> crlData;
00098   PRBool importSuccessful;
00099   PRInt32 errorCode;
00100   nsString errorMessage;
00101   
00102   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
00103   if (NS_FAILED(rv)) return rv;
00104                 
00105   aURI->GetSpec(url);
00106   arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00107   if (!arena) {
00108     goto loser;
00109   }
00110   memset(&sd, 0, sizeof(sd));
00111 
00112   derCrl.data = (unsigned char*)aData;
00113   derCrl.len = aLength;
00114   sec_rv = CERT_KeyFromDERCrl(arena, &derCrl, &derName);
00115   if (sec_rv != SECSuccess) {
00116     goto loser;
00117   }
00118 
00119   caCert = CERT_FindCertByName(CERT_GetDefaultCertDB(), &derName);
00120   if (!caCert) {
00121     if (aType == SEC_KRL_TYPE){
00122       goto loser;
00123     }
00124   } else {
00125     sec_rv = SEC_ASN1DecodeItem(arena,
00126                             &sd, SEC_ASN1_GET(CERT_SignedDataTemplate), 
00127                             &derCrl);
00128     if (sec_rv != SECSuccess) {
00129       goto loser;
00130     }
00131     sec_rv = CERT_VerifySignedData(&sd, caCert, PR_Now(),
00132                                nsnull);
00133     if (sec_rv != SECSuccess) {
00134       goto loser;
00135     }
00136   }
00137   
00138   crl = SEC_NewCrl(CERT_GetDefaultCertDB(), NS_CONST_CAST(char*, url.get()), &derCrl,
00139                    aType);
00140   
00141   if (!crl) {
00142     goto loser;
00143   }
00144 
00145   crlData = new nsCRLInfo(crl);
00146   SSL_ClearSessionCache();
00147   SEC_DestroyCrl(crl);
00148   
00149   importSuccessful = PR_TRUE;
00150   goto done;
00151 
00152 loser:
00153   importSuccessful = PR_FALSE;
00154   errorCode = PR_GetError();
00155   switch (errorCode) {
00156     case SEC_ERROR_CRL_EXPIRED:
00157       nssComponent->GetPIPNSSBundleString("CrlImportFailureExpired", errorMessage);
00158       break;
00159 
00160        case SEC_ERROR_CRL_BAD_SIGNATURE:
00161       nssComponent->GetPIPNSSBundleString("CrlImportFailureBadSignature", errorMessage);
00162       break;
00163 
00164        case SEC_ERROR_CRL_INVALID:
00165       nssComponent->GetPIPNSSBundleString("CrlImportFailureInvalid", errorMessage);
00166       break;
00167 
00168        case SEC_ERROR_OLD_CRL:
00169       nssComponent->GetPIPNSSBundleString("CrlImportFailureOld", errorMessage);
00170       break;
00171 
00172        case SEC_ERROR_CRL_NOT_YET_VALID:
00173       nssComponent->GetPIPNSSBundleString("CrlImportFailureNotYetValid", errorMessage);
00174       break;
00175 
00176     default:
00177       nssComponent->GetPIPNSSBundleString("CrlImportFailureReasonUnknown", errorMessage);
00178       errorMessage.AppendInt(errorCode,16);
00179       break;
00180   }
00181 
00182 done:
00183           
00184   if(!doSilentDonwload){
00185     if (!importSuccessful){
00186       nsString message;
00187       nsString temp;
00188       nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
00189       nsCOMPtr<nsIPrompt> prompter;
00190       if (wwatch){
00191         wwatch->GetNewPrompter(0, getter_AddRefs(prompter));
00192         nssComponent->GetPIPNSSBundleString("CrlImportFailure1", message);
00193         message.Append(NS_LITERAL_STRING("\n").get());
00194         message.Append(errorMessage);
00195         nssComponent->GetPIPNSSBundleString("CrlImportFailure2", temp);
00196         message.Append(NS_LITERAL_STRING("\n").get());
00197         message.Append(temp);
00198      
00199         if(prompter) {
00200           nsPSMUITracker tracker;
00201           if (!tracker.isUIForbidden()) {
00202             prompter->Alert(0, message.get());
00203           }
00204         }
00205       }
00206     } else {
00207       nsCOMPtr<nsICertificateDialogs> certDialogs;
00208       // Not being able to display the success dialog should not
00209       // be a fatal error, so don't return a failure code.
00210       {
00211         nsPSMUITracker tracker;
00212         if (tracker.isUIForbidden()) {
00213           rv = NS_ERROR_NOT_AVAILABLE;
00214         }
00215         else {
00216           rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
00217             NS_GET_IID(nsICertificateDialogs), NS_CERTIFICATEDIALOGS_CONTRACTID);
00218         }
00219       }
00220       if (NS_SUCCEEDED(rv)) {
00221         nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
00222         certDialogs->CrlImportStatusDialog(cxt, crlData);
00223       }
00224     }
00225   } else {
00226     if(crlKey == nsnull){
00227       return NS_ERROR_FAILURE;
00228     }
00229     nsCOMPtr<nsIPrefService> prefSvc = do_GetService(NS_PREFSERVICE_CONTRACTID,&rv);
00230     nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID,&rv);
00231     if (NS_FAILED(rv)){
00232       return rv;
00233     }
00234     
00235     nsCAutoString updateErrCntPrefStr(CRL_AUTOUPDATE_ERRCNT_PREF);
00236     updateErrCntPrefStr.AppendWithConversion(crlKey);
00237     if(importSuccessful){
00238       PRUnichar *updateTime;
00239       nsCAutoString updateTimeStr;
00240       nsCString updateURL;
00241       PRInt32 timingTypePref;
00242       double dayCnt;
00243       char *dayCntStr;
00244       nsCAutoString updateTypePrefStr(CRL_AUTOUPDATE_TIMIINGTYPE_PREF);
00245       nsCAutoString updateTimePrefStr(CRL_AUTOUPDATE_TIME_PREF);
00246       nsCAutoString updateUrlPrefStr(CRL_AUTOUPDATE_URL_PREF);
00247       nsCAutoString updateDayCntPrefStr(CRL_AUTOUPDATE_DAYCNT_PREF);
00248       nsCAutoString updateFreqCntPrefStr(CRL_AUTOUPDATE_FREQCNT_PREF);
00249       updateTypePrefStr.AppendWithConversion(crlKey);
00250       updateTimePrefStr.AppendWithConversion(crlKey);
00251       updateUrlPrefStr.AppendWithConversion(crlKey);
00252       updateDayCntPrefStr.AppendWithConversion(crlKey);
00253       updateFreqCntPrefStr.AppendWithConversion(crlKey);
00254 
00255       pref->GetIntPref(updateTypePrefStr.get(),&timingTypePref);
00256       
00257       //Compute and update the next download instant
00258       if(timingTypePref == TYPE_AUTOUPDATE_TIME_BASED){
00259         pref->GetCharPref(updateDayCntPrefStr.get(),&dayCntStr);
00260       }else{
00261         pref->GetCharPref(updateFreqCntPrefStr.get(),&dayCntStr);
00262       }
00263       dayCnt = atof(dayCntStr);
00264       nsMemory::Free(dayCntStr);
00265 
00266       PRBool toBeRescheduled = PR_FALSE;
00267       if(NS_SUCCEEDED(ComputeNextAutoUpdateTime(crlData, timingTypePref, dayCnt, &updateTime))){
00268         updateTimeStr.AssignWithConversion(updateTime);
00269         nsMemory::Free(updateTime);
00270         pref->SetCharPref(updateTimePrefStr.get(),updateTimeStr.get());
00271         //Now, check if this update time is already in the past. This would
00272         //imply we have downloaded the same crl, or there is something wrong
00273         //with the next update date. We will not reschedule this crl in this
00274         //session anymore - or else, we land into a loop. It would anyway be
00275         //imported once the browser is restarted.
00276         PRTime nextTime;
00277         PR_ParseTimeString(updateTimeStr.get(),PR_TRUE, &nextTime);
00278         if(LL_CMP(nextTime, > , PR_Now())){
00279           toBeRescheduled = PR_TRUE;
00280         }
00281       }
00282       
00283       //Update the url to download from, next time
00284       crlData->GetLastFetchURL(updateURL);
00285       pref->SetCharPref(updateUrlPrefStr.get(),updateURL.get());
00286       
00287       pref->SetIntPref(updateErrCntPrefStr.get(),0);
00288       
00289       if(toBeRescheduled == PR_TRUE){
00290         nsAutoString hashKey(crlKey);
00291         nssComponent->RemoveCrlFromList(hashKey);
00292         nssComponent->DefineNextTimer();
00293       }
00294 
00295     } else{
00296       PRInt32 errCnt;
00297       nsCAutoString errMsg;
00298       nsCAutoString updateErrDetailPrefStr(CRL_AUTOUPDATE_ERRDETAIL_PREF);
00299       updateErrDetailPrefStr.AppendWithConversion(crlKey);
00300       errMsg.AssignWithConversion(errorMessage.get());
00301       rv = pref->GetIntPref(updateErrCntPrefStr.get(),&errCnt);
00302       if(NS_FAILED(rv))
00303         errCnt = 0;
00304 
00305       pref->SetIntPref(updateErrCntPrefStr.get(),errCnt+1);
00306       pref->SetCharPref(updateErrDetailPrefStr.get(),errMsg.get());
00307     }
00308     prefSvc->SavePrefFile(nsnull);
00309   }
00310 
00311   return rv;
00312 }
00313 
00314 NS_IMETHODIMP 
00315 nsCRLManager::UpdateCRLFromURL( const PRUnichar *url, const PRUnichar* key, PRBool *res)
00316 {
00317   nsresult rv;
00318   nsAutoString downloadUrl(url);
00319   nsAutoString dbKey(key);
00320   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
00321   if(NS_FAILED(rv)){
00322     *res = PR_FALSE;
00323     return rv;
00324   }
00325 
00326   rv = nssComponent->DownloadCRLDirectly(downloadUrl, dbKey);
00327   if(NS_FAILED(rv)){
00328     *res = PR_FALSE;
00329   } else {
00330     *res = PR_TRUE;
00331   }
00332   return NS_OK;
00333 
00334 }
00335 
00336 NS_IMETHODIMP 
00337 nsCRLManager::RescheduleCRLAutoUpdate(void)
00338 {
00339   nsresult rv;
00340   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
00341   if(NS_FAILED(rv)){
00342     return rv;
00343   }
00344   rv = nssComponent->DefineNextTimer();
00345   return rv;
00346 }
00347 
00348 /*
00349  * getCRLs
00350  *
00351  * Export a set of certs and keys from the database to a PKCS#12 file.
00352 */
00353 
00354 NS_IMETHODIMP 
00355 nsCRLManager::GetCrls(nsIArray ** aCrls)
00356 {
00357   nsNSSShutDownPreventionLock locker;
00358   SECStatus sec_rv;
00359   CERTCrlHeadNode *head = nsnull;
00360   CERTCrlNode *node = nsnull;
00361   nsCOMPtr<nsIMutableArray> crlsArray;
00362   nsresult rv;
00363   rv = NS_NewArray(getter_AddRefs(crlsArray));
00364   if (NS_FAILED(rv)) {
00365     return rv;
00366   }
00367 
00368   // Get the list of certs //
00369   sec_rv = SEC_LookupCrls(CERT_GetDefaultCertDB(), &head, -1);
00370   if (sec_rv != SECSuccess) {
00371     goto loser;
00372   }
00373 
00374   if (head) {
00375     for (node=head->first; node != nsnull; node = node->next) {
00376 
00377       nsCOMPtr<nsICRLInfo> entry = new nsCRLInfo((node->crl));
00378       crlsArray->AppendElement(entry, PR_FALSE);
00379     }
00380     PORT_FreeArena(head->arena, PR_FALSE);
00381   }
00382 
00383   *aCrls = crlsArray;
00384   NS_IF_ADDREF(*aCrls);
00385   return NS_OK;
00386 loser:
00387   return NS_ERROR_FAILURE;;
00388 }
00389 
00390 /*
00391  * deletetCrl
00392  *
00393  * Delete a Crl entry from the cert db.
00394 */
00395 NS_IMETHODIMP 
00396 nsCRLManager::DeleteCrl(PRUint32 aCrlIndex)
00397 {
00398   nsNSSShutDownPreventionLock locker;
00399   CERTSignedCrl *realCrl = nsnull;
00400   CERTCrlHeadNode *head = nsnull;
00401   CERTCrlNode *node = nsnull;
00402   SECStatus sec_rv;
00403   PRUint32 i;
00404 
00405   // Get the list of certs //
00406   sec_rv = SEC_LookupCrls(CERT_GetDefaultCertDB(), &head, -1);
00407   if (sec_rv != SECSuccess) {
00408     goto loser;
00409   }
00410 
00411   if (head) {
00412     for (i = 0, node=head->first; node != nsnull; i++, node = node->next) {
00413       if (i != aCrlIndex) {
00414         continue;
00415       }
00416       realCrl = SEC_FindCrlByName(CERT_GetDefaultCertDB(), &(node->crl->crl.derName), node->type);
00417       SEC_DeletePermCRL(realCrl);
00418       SEC_DestroyCrl(realCrl);
00419       SSL_ClearSessionCache();
00420     }
00421     PORT_FreeArena(head->arena, PR_FALSE);
00422   }
00423   return NS_OK;
00424 loser:
00425   return NS_ERROR_FAILURE;;
00426 }
00427 
00428 NS_IMETHODIMP
00429 nsCRLManager::ComputeNextAutoUpdateTime(nsICRLInfo *info, 
00430   PRUint32 autoUpdateType, double dayCnt, PRUnichar **nextAutoUpdate)
00431 {
00432   if (!info)
00433     return NS_ERROR_FAILURE;
00434 
00435   PRTime microsecInDayCnt;
00436   PRTime now = PR_Now();
00437   PRTime tempTime;
00438   PRInt64 diff = 0;
00439   PRInt64 secsInDay = 86400UL;
00440   PRInt64 temp;
00441   PRInt64 cycleCnt = 0;
00442   PRInt64 secsInDayCnt;
00443   PRFloat64 tmpData;
00444   
00445   LL_L2F(tmpData,secsInDay);
00446   LL_MUL(tmpData,dayCnt,tmpData);
00447   LL_F2L(secsInDayCnt,tmpData);
00448   LL_MUL(microsecInDayCnt, secsInDayCnt, PR_USEC_PER_SEC);
00449     
00450   PRTime lastUpdate;
00451   PRTime nextUpdate;
00452   
00453   nsresult rv;
00454 
00455   rv = info->GetLastUpdate(&lastUpdate);
00456   if (NS_FAILED(rv))
00457     return rv;
00458 
00459   rv = info->GetNextUpdate(&nextUpdate);
00460   if (NS_FAILED(rv))
00461     return rv;
00462 
00463   switch (autoUpdateType) {
00464   case TYPE_AUTOUPDATE_FREQ_BASED:
00465     LL_SUB(diff, now, lastUpdate);             //diff is the no of micro sec between now and last update
00466     LL_DIV(cycleCnt, diff, microsecInDayCnt);   //temp is the number of full cycles from lst update
00467     LL_MOD(temp, diff, microsecInDayCnt);
00468     if(!(LL_IS_ZERO(temp))) {
00469       LL_ADD(cycleCnt,cycleCnt,1);            //no of complete cycles till next autoupdate instant
00470     }
00471     LL_MUL(temp,cycleCnt,microsecInDayCnt);    //micro secs from last update
00472     LL_ADD(tempTime, lastUpdate, temp);
00473     break;  
00474   case TYPE_AUTOUPDATE_TIME_BASED:
00475     LL_SUB(tempTime, nextUpdate, microsecInDayCnt);
00476     break;
00477   default:
00478     return NS_ERROR_NOT_IMPLEMENTED;
00479   }
00480 
00481   //Now, a basic constraing is that the next auto update date can never be after
00482   //next update, if one is defined
00483   if(LL_CMP(nextUpdate , > , 0 )) {
00484     if(LL_CMP(tempTime , > , nextUpdate)) {
00485       tempTime = nextUpdate;
00486     }
00487   }
00488 
00489   nsAutoString nextAutoUpdateDate;
00490   PRExplodedTime explodedTime;
00491   nsCOMPtr<nsIDateTimeFormat> dateFormatter = do_CreateInstance(kDateTimeFormatCID, &rv);
00492   if (NS_FAILED(rv))
00493     return rv;
00494   PR_ExplodeTime(tempTime, PR_GMTParameters, &explodedTime);
00495   dateFormatter->FormatPRExplodedTime(nsnull, kDateFormatShort, kTimeFormatSeconds,
00496                                       &explodedTime, nextAutoUpdateDate);
00497   *nextAutoUpdate = ToNewUnicode(nextAutoUpdateDate);
00498 
00499   return NS_OK;
00500 }
00501