1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is the Netscape security libraries.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Netscape Communications Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2000
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "nsCRLManager.h"
38 : #include "nsCRLInfo.h"
39 :
40 : #include "nsCOMPtr.h"
41 : #include "nsIDateTimeFormat.h"
42 : #include "nsDateTimeFormatCID.h"
43 : #include "nsComponentManagerUtils.h"
44 : #include "nsReadableUtils.h"
45 : #include "nsNSSComponent.h"
46 : #include "nsCOMPtr.h"
47 : #include "nsICertificateDialogs.h"
48 : #include "nsIMutableArray.h"
49 : #include "nsIPrefService.h"
50 : #include "nsIPrefBranch.h"
51 : #include "nsNSSShutDown.h"
52 : #include "nsThreadUtils.h"
53 :
54 : #include "nsNSSCertHeader.h"
55 :
56 : #include "nspr.h"
57 : extern "C" {
58 : #include "pk11func.h"
59 : #include "certdb.h"
60 : #include "cert.h"
61 : #include "secerr.h"
62 : #include "nssb64.h"
63 : #include "secasn1.h"
64 : #include "secder.h"
65 : }
66 : #include "ssl.h"
67 : #include "ocsp.h"
68 : #include "plbase64.h"
69 :
70 : static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
71 :
72 0 : NS_IMPL_ISUPPORTS1(nsCRLManager, nsICRLManager)
73 :
74 0 : nsCRLManager::nsCRLManager()
75 : {
76 0 : }
77 :
78 0 : nsCRLManager::~nsCRLManager()
79 : {
80 0 : }
81 :
82 : NS_IMETHODIMP
83 0 : nsCRLManager::ImportCrl (PRUint8 *aData, PRUint32 aLength, nsIURI * aURI, PRUint32 aType, bool doSilentDownload, const PRUnichar* crlKey)
84 : {
85 0 : if (!NS_IsMainThread()) {
86 0 : NS_ERROR("nsCRLManager::ImportCrl called off the main thread");
87 0 : return NS_ERROR_NOT_SAME_THREAD;
88 : }
89 :
90 0 : nsNSSShutDownPreventionLock locker;
91 : nsresult rv;
92 0 : PRArenaPool *arena = NULL;
93 : CERTCertificate *caCert;
94 0 : SECItem derName = { siBuffer, NULL, 0 };
95 : SECItem derCrl;
96 : CERTSignedData sd;
97 : SECStatus sec_rv;
98 : CERTSignedCrl *crl;
99 0 : nsCAutoString url;
100 0 : nsCOMPtr<nsICRLInfo> crlData;
101 : bool importSuccessful;
102 : PRInt32 errorCode;
103 0 : nsString errorMessage;
104 :
105 0 : nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
106 0 : if (NS_FAILED(rv)) return rv;
107 :
108 0 : aURI->GetSpec(url);
109 0 : arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
110 0 : if (!arena) {
111 0 : goto loser;
112 : }
113 0 : memset(&sd, 0, sizeof(sd));
114 :
115 0 : derCrl.data = (unsigned char*)aData;
116 0 : derCrl.len = aLength;
117 0 : sec_rv = CERT_KeyFromDERCrl(arena, &derCrl, &derName);
118 0 : if (sec_rv != SECSuccess) {
119 0 : goto loser;
120 : }
121 :
122 0 : caCert = CERT_FindCertByName(CERT_GetDefaultCertDB(), &derName);
123 0 : if (!caCert) {
124 0 : if (aType == SEC_KRL_TYPE){
125 0 : goto loser;
126 : }
127 : } else {
128 : sec_rv = SEC_ASN1DecodeItem(arena,
129 : &sd, SEC_ASN1_GET(CERT_SignedDataTemplate),
130 0 : &derCrl);
131 0 : if (sec_rv != SECSuccess) {
132 0 : goto loser;
133 : }
134 : sec_rv = CERT_VerifySignedData(&sd, caCert, PR_Now(),
135 0 : nsnull);
136 0 : if (sec_rv != SECSuccess) {
137 0 : goto loser;
138 : }
139 : }
140 :
141 0 : crl = SEC_NewCrl(CERT_GetDefaultCertDB(), const_cast<char*>(url.get()), &derCrl,
142 0 : aType);
143 :
144 0 : if (!crl) {
145 0 : goto loser;
146 : }
147 :
148 0 : crlData = new nsCRLInfo(crl);
149 0 : SSL_ClearSessionCache();
150 0 : SEC_DestroyCrl(crl);
151 :
152 0 : importSuccessful = true;
153 0 : goto done;
154 :
155 : loser:
156 0 : importSuccessful = false;
157 0 : errorCode = PR_GetError();
158 0 : switch (errorCode) {
159 : case SEC_ERROR_CRL_EXPIRED:
160 0 : nssComponent->GetPIPNSSBundleString("CrlImportFailureExpired", errorMessage);
161 0 : break;
162 :
163 : case SEC_ERROR_CRL_BAD_SIGNATURE:
164 0 : nssComponent->GetPIPNSSBundleString("CrlImportFailureBadSignature", errorMessage);
165 0 : break;
166 :
167 : case SEC_ERROR_CRL_INVALID:
168 0 : nssComponent->GetPIPNSSBundleString("CrlImportFailureInvalid", errorMessage);
169 0 : break;
170 :
171 : case SEC_ERROR_OLD_CRL:
172 0 : nssComponent->GetPIPNSSBundleString("CrlImportFailureOld", errorMessage);
173 0 : break;
174 :
175 : case SEC_ERROR_CRL_NOT_YET_VALID:
176 0 : nssComponent->GetPIPNSSBundleString("CrlImportFailureNotYetValid", errorMessage);
177 0 : break;
178 :
179 : default:
180 0 : nssComponent->GetPIPNSSBundleString("CrlImportFailureReasonUnknown", errorMessage);
181 0 : errorMessage.AppendInt(errorCode,16);
182 0 : break;
183 : }
184 :
185 : done:
186 :
187 0 : if(!doSilentDownload){
188 0 : if (!importSuccessful){
189 0 : nsString message;
190 0 : nsString temp;
191 0 : nssComponent->GetPIPNSSBundleString("CrlImportFailure1x", message);
192 0 : message.Append(NS_LITERAL_STRING("\n").get());
193 0 : message.Append(errorMessage);
194 0 : nssComponent->GetPIPNSSBundleString("CrlImportFailure2", temp);
195 0 : message.Append(NS_LITERAL_STRING("\n").get());
196 0 : message.Append(temp);
197 :
198 0 : nsNSSComponent::ShowAlertWithConstructedString(message);
199 : } else {
200 0 : nsCOMPtr<nsICertificateDialogs> certDialogs;
201 : // Not being able to display the success dialog should not
202 : // be a fatal error, so don't return a failure code.
203 : {
204 0 : nsPSMUITracker tracker;
205 0 : if (tracker.isUIForbidden()) {
206 0 : rv = NS_ERROR_NOT_AVAILABLE;
207 : }
208 : else {
209 0 : rv = ::getNSSDialogs(getter_AddRefs(certDialogs),
210 0 : NS_GET_IID(nsICertificateDialogs), NS_CERTIFICATEDIALOGS_CONTRACTID);
211 : }
212 : }
213 0 : if (NS_SUCCEEDED(rv)) {
214 0 : nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
215 0 : certDialogs->CrlImportStatusDialog(cxt, crlData);
216 : }
217 : }
218 : } else {
219 0 : if(crlKey == nsnull){
220 0 : return NS_ERROR_FAILURE;
221 : }
222 0 : nsCOMPtr<nsIPrefService> prefSvc = do_GetService(NS_PREFSERVICE_CONTRACTID,&rv);
223 0 : nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID,&rv);
224 0 : if (NS_FAILED(rv)){
225 0 : return rv;
226 : }
227 :
228 0 : nsCAutoString updateErrCntPrefStr(CRL_AUTOUPDATE_ERRCNT_PREF);
229 0 : updateErrCntPrefStr.AppendWithConversion(crlKey);
230 0 : if(importSuccessful){
231 : PRUnichar *updateTime;
232 0 : nsCAutoString updateTimeStr;
233 0 : nsCString updateURL;
234 : PRInt32 timingTypePref;
235 : double dayCnt;
236 : char *dayCntStr;
237 0 : nsCAutoString updateTypePrefStr(CRL_AUTOUPDATE_TIMIINGTYPE_PREF);
238 0 : nsCAutoString updateTimePrefStr(CRL_AUTOUPDATE_TIME_PREF);
239 0 : nsCAutoString updateUrlPrefStr(CRL_AUTOUPDATE_URL_PREF);
240 0 : nsCAutoString updateDayCntPrefStr(CRL_AUTOUPDATE_DAYCNT_PREF);
241 0 : nsCAutoString updateFreqCntPrefStr(CRL_AUTOUPDATE_FREQCNT_PREF);
242 0 : updateTypePrefStr.AppendWithConversion(crlKey);
243 0 : updateTimePrefStr.AppendWithConversion(crlKey);
244 0 : updateUrlPrefStr.AppendWithConversion(crlKey);
245 0 : updateDayCntPrefStr.AppendWithConversion(crlKey);
246 0 : updateFreqCntPrefStr.AppendWithConversion(crlKey);
247 :
248 0 : pref->GetIntPref(updateTypePrefStr.get(),&timingTypePref);
249 :
250 : //Compute and update the next download instant
251 0 : if(timingTypePref == TYPE_AUTOUPDATE_TIME_BASED){
252 0 : pref->GetCharPref(updateDayCntPrefStr.get(),&dayCntStr);
253 : }else{
254 0 : pref->GetCharPref(updateFreqCntPrefStr.get(),&dayCntStr);
255 : }
256 0 : dayCnt = atof(dayCntStr);
257 0 : nsMemory::Free(dayCntStr);
258 :
259 0 : bool toBeRescheduled = false;
260 0 : if(NS_SUCCEEDED(ComputeNextAutoUpdateTime(crlData, timingTypePref, dayCnt, &updateTime))){
261 0 : updateTimeStr.AssignWithConversion(updateTime);
262 0 : nsMemory::Free(updateTime);
263 0 : pref->SetCharPref(updateTimePrefStr.get(),updateTimeStr.get());
264 : //Now, check if this update time is already in the past. This would
265 : //imply we have downloaded the same crl, or there is something wrong
266 : //with the next update date. We will not reschedule this crl in this
267 : //session anymore - or else, we land into a loop. It would anyway be
268 : //imported once the browser is restarted.
269 : PRTime nextTime;
270 0 : PR_ParseTimeString(updateTimeStr.get(),true, &nextTime);
271 0 : if(LL_CMP(nextTime, > , PR_Now())){
272 0 : toBeRescheduled = true;
273 : }
274 : }
275 :
276 : //Update the url to download from, next time
277 0 : crlData->GetLastFetchURL(updateURL);
278 0 : pref->SetCharPref(updateUrlPrefStr.get(),updateURL.get());
279 :
280 0 : pref->SetIntPref(updateErrCntPrefStr.get(),0);
281 :
282 0 : if (toBeRescheduled) {
283 0 : nsAutoString hashKey(crlKey);
284 0 : nssComponent->RemoveCrlFromList(hashKey);
285 0 : nssComponent->DefineNextTimer();
286 : }
287 :
288 : } else{
289 : PRInt32 errCnt;
290 0 : nsCAutoString errMsg;
291 0 : nsCAutoString updateErrDetailPrefStr(CRL_AUTOUPDATE_ERRDETAIL_PREF);
292 0 : updateErrDetailPrefStr.AppendWithConversion(crlKey);
293 0 : errMsg.AssignWithConversion(errorMessage.get());
294 0 : rv = pref->GetIntPref(updateErrCntPrefStr.get(),&errCnt);
295 0 : if(NS_FAILED(rv))
296 0 : errCnt = 0;
297 :
298 0 : pref->SetIntPref(updateErrCntPrefStr.get(),errCnt+1);
299 0 : pref->SetCharPref(updateErrDetailPrefStr.get(),errMsg.get());
300 : }
301 0 : prefSvc->SavePrefFile(nsnull);
302 : }
303 :
304 0 : return rv;
305 : }
306 :
307 : NS_IMETHODIMP
308 0 : nsCRLManager::UpdateCRLFromURL( const PRUnichar *url, const PRUnichar* key, bool *res)
309 : {
310 : nsresult rv;
311 0 : nsAutoString downloadUrl(url);
312 0 : nsAutoString dbKey(key);
313 0 : nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
314 0 : if(NS_FAILED(rv)){
315 0 : *res = false;
316 0 : return rv;
317 : }
318 :
319 0 : rv = nssComponent->DownloadCRLDirectly(downloadUrl, dbKey);
320 0 : if(NS_FAILED(rv)){
321 0 : *res = false;
322 : } else {
323 0 : *res = true;
324 : }
325 0 : return NS_OK;
326 :
327 : }
328 :
329 : NS_IMETHODIMP
330 0 : nsCRLManager::RescheduleCRLAutoUpdate(void)
331 : {
332 : nsresult rv;
333 0 : nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
334 0 : if(NS_FAILED(rv)){
335 0 : return rv;
336 : }
337 0 : rv = nssComponent->DefineNextTimer();
338 0 : return rv;
339 : }
340 :
341 : /**
342 : * getCRLs
343 : *
344 : * Export a set of certs and keys from the database to a PKCS#12 file.
345 : */
346 : NS_IMETHODIMP
347 0 : nsCRLManager::GetCrls(nsIArray ** aCrls)
348 : {
349 0 : nsNSSShutDownPreventionLock locker;
350 : SECStatus sec_rv;
351 0 : CERTCrlHeadNode *head = nsnull;
352 0 : CERTCrlNode *node = nsnull;
353 : nsresult rv;
354 : nsCOMPtr<nsIMutableArray> crlsArray =
355 0 : do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
356 0 : if (NS_FAILED(rv)) {
357 0 : return rv;
358 : }
359 :
360 : // Get the list of certs //
361 0 : sec_rv = SEC_LookupCrls(CERT_GetDefaultCertDB(), &head, -1);
362 0 : if (sec_rv != SECSuccess) {
363 0 : return NS_ERROR_FAILURE;
364 : }
365 :
366 0 : if (head) {
367 0 : for (node=head->first; node != nsnull; node = node->next) {
368 :
369 0 : nsCOMPtr<nsICRLInfo> entry = new nsCRLInfo((node->crl));
370 0 : crlsArray->AppendElement(entry, false);
371 : }
372 0 : PORT_FreeArena(head->arena, false);
373 : }
374 :
375 0 : *aCrls = crlsArray;
376 0 : NS_IF_ADDREF(*aCrls);
377 0 : return NS_OK;
378 : }
379 :
380 : /**
381 : * deleteCrl
382 : *
383 : * Delete a Crl entry from the cert db.
384 : */
385 : NS_IMETHODIMP
386 0 : nsCRLManager::DeleteCrl(PRUint32 aCrlIndex)
387 : {
388 0 : nsNSSShutDownPreventionLock locker;
389 0 : CERTSignedCrl *realCrl = nsnull;
390 0 : CERTCrlHeadNode *head = nsnull;
391 0 : CERTCrlNode *node = nsnull;
392 : SECStatus sec_rv;
393 : PRUint32 i;
394 :
395 : // Get the list of certs //
396 0 : sec_rv = SEC_LookupCrls(CERT_GetDefaultCertDB(), &head, -1);
397 0 : if (sec_rv != SECSuccess) {
398 0 : return NS_ERROR_FAILURE;
399 : }
400 :
401 0 : if (head) {
402 0 : for (i = 0, node=head->first; node != nsnull; i++, node = node->next) {
403 0 : if (i != aCrlIndex) {
404 0 : continue;
405 : }
406 0 : realCrl = SEC_FindCrlByName(CERT_GetDefaultCertDB(), &(node->crl->crl.derName), node->type);
407 0 : SEC_DeletePermCRL(realCrl);
408 0 : SEC_DestroyCrl(realCrl);
409 0 : SSL_ClearSessionCache();
410 : }
411 0 : PORT_FreeArena(head->arena, false);
412 : }
413 0 : return NS_OK;
414 : }
415 :
416 : NS_IMETHODIMP
417 0 : nsCRLManager::ComputeNextAutoUpdateTime(nsICRLInfo *info,
418 : PRUint32 autoUpdateType, double dayCnt, PRUnichar **nextAutoUpdate)
419 : {
420 0 : if (!info)
421 0 : return NS_ERROR_FAILURE;
422 :
423 : PRTime microsecInDayCnt;
424 0 : PRTime now = PR_Now();
425 : PRTime tempTime;
426 0 : PRInt64 diff = 0;
427 0 : PRInt64 secsInDay = 86400UL;
428 : PRInt64 temp;
429 0 : PRInt64 cycleCnt = 0;
430 : PRInt64 secsInDayCnt;
431 : PRFloat64 tmpData;
432 :
433 0 : LL_L2F(tmpData,secsInDay);
434 0 : LL_MUL(tmpData,dayCnt,tmpData);
435 0 : LL_F2L(secsInDayCnt,tmpData);
436 0 : LL_MUL(microsecInDayCnt, secsInDayCnt, PR_USEC_PER_SEC);
437 :
438 : PRTime lastUpdate;
439 : PRTime nextUpdate;
440 :
441 : nsresult rv;
442 :
443 0 : rv = info->GetLastUpdate(&lastUpdate);
444 0 : if (NS_FAILED(rv))
445 0 : return rv;
446 :
447 0 : rv = info->GetNextUpdate(&nextUpdate);
448 0 : if (NS_FAILED(rv))
449 0 : return rv;
450 :
451 0 : switch (autoUpdateType) {
452 : case TYPE_AUTOUPDATE_FREQ_BASED:
453 0 : LL_SUB(diff, now, lastUpdate); //diff is the no of micro sec between now and last update
454 0 : LL_DIV(cycleCnt, diff, microsecInDayCnt); //temp is the number of full cycles from lst update
455 0 : LL_MOD(temp, diff, microsecInDayCnt);
456 0 : if(!(LL_IS_ZERO(temp))) {
457 0 : LL_ADD(cycleCnt,cycleCnt,1); //no of complete cycles till next autoupdate instant
458 : }
459 0 : LL_MUL(temp,cycleCnt,microsecInDayCnt); //micro secs from last update
460 0 : LL_ADD(tempTime, lastUpdate, temp);
461 0 : break;
462 : case TYPE_AUTOUPDATE_TIME_BASED:
463 0 : LL_SUB(tempTime, nextUpdate, microsecInDayCnt);
464 0 : break;
465 : default:
466 0 : return NS_ERROR_NOT_IMPLEMENTED;
467 : }
468 :
469 : //Now, a basic constraing is that the next auto update date can never be after
470 : //next update, if one is defined
471 0 : if(LL_CMP(nextUpdate , > , 0 )) {
472 0 : if(LL_CMP(tempTime , > , nextUpdate)) {
473 0 : tempTime = nextUpdate;
474 : }
475 : }
476 :
477 0 : nsAutoString nextAutoUpdateDate;
478 : PRExplodedTime explodedTime;
479 0 : nsCOMPtr<nsIDateTimeFormat> dateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv);
480 0 : if (NS_FAILED(rv))
481 0 : return rv;
482 0 : PR_ExplodeTime(tempTime, PR_GMTParameters, &explodedTime);
483 0 : dateFormatter->FormatPRExplodedTime(nsnull, kDateFormatShort, kTimeFormatSeconds,
484 0 : &explodedTime, nextAutoUpdateDate);
485 0 : *nextAutoUpdate = ToNewUnicode(nextAutoUpdateDate);
486 :
487 0 : return NS_OK;
488 : }
489 :
|