1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Red Hat, Inc.
20 : * Portions created by the Initial Developer are Copyright (C) 2006
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Kai Engert <kengert@redhat.com>
25 : * Ehsan Akhgari <ehsan.akhgari@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsCertOverrideService.h"
42 : #include "nsIX509Cert.h"
43 : #include "nsNSSCertificate.h"
44 : #include "nsCRT.h"
45 : #include "nsAppDirectoryServiceDefs.h"
46 : #include "nsStreamUtils.h"
47 : #include "nsNetUtil.h"
48 : #include "nsILineInputStream.h"
49 : #include "nsIObserver.h"
50 : #include "nsIObserverService.h"
51 : #include "nsISupportsPrimitives.h"
52 : #include "nsPromiseFlatString.h"
53 : #include "nsThreadUtils.h"
54 : #include "nsStringBuffer.h"
55 : #include "nsAutoPtr.h"
56 : #include "nspr.h"
57 : #include "pk11pub.h"
58 : #include "certdb.h"
59 : #include "sechash.h"
60 : #include "ssl.h" // For SSL_ClearSessionCache
61 :
62 : #include "nsNSSCleaner.h"
63 0 : NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
64 :
65 : using namespace mozilla;
66 :
67 : static const char kCertOverrideFileName[] = "cert_override.txt";
68 :
69 : void
70 0 : nsCertOverride::convertBitsToString(OverrideBits ob, nsACString &str)
71 : {
72 0 : str.Truncate();
73 :
74 0 : if (ob & ob_Mismatch)
75 0 : str.Append('M');
76 :
77 0 : if (ob & ob_Untrusted)
78 0 : str.Append('U');
79 :
80 0 : if (ob & ob_Time_error)
81 0 : str.Append('T');
82 0 : }
83 :
84 : void
85 0 : nsCertOverride::convertStringToBits(const nsACString &str, OverrideBits &ob)
86 : {
87 0 : const nsPromiseFlatCString &flat = PromiseFlatCString(str);
88 0 : const char *walk = flat.get();
89 :
90 0 : ob = ob_None;
91 :
92 0 : for ( ; *walk; ++walk)
93 : {
94 0 : switch (*walk)
95 : {
96 : case 'm':
97 : case 'M':
98 0 : ob = (OverrideBits)(ob | ob_Mismatch);
99 0 : break;
100 :
101 : case 'u':
102 : case 'U':
103 0 : ob = (OverrideBits)(ob | ob_Untrusted);
104 0 : break;
105 :
106 : case 't':
107 : case 'T':
108 0 : ob = (OverrideBits)(ob | ob_Time_error);
109 0 : break;
110 :
111 : default:
112 0 : break;
113 : }
114 : }
115 0 : }
116 :
117 1335 : NS_IMPL_THREADSAFE_ISUPPORTS3(nsCertOverrideService,
118 : nsICertOverrideService,
119 : nsIObserver,
120 : nsISupportsWeakReference)
121 :
122 35 : nsCertOverrideService::nsCertOverrideService()
123 35 : : monitor("nsCertOverrideService.monitor")
124 : {
125 35 : }
126 :
127 35 : nsCertOverrideService::~nsCertOverrideService()
128 : {
129 35 : }
130 :
131 : nsresult
132 35 : nsCertOverrideService::Init()
133 : {
134 35 : if (!NS_IsMainThread()) {
135 0 : NS_NOTREACHED("nsCertOverrideService initialized off main thread");
136 0 : return NS_ERROR_NOT_SAME_THREAD;
137 : }
138 :
139 35 : if (!mSettingsTable.Init())
140 0 : return NS_ERROR_OUT_OF_MEMORY;
141 :
142 35 : mOidTagForStoringNewHashes = SEC_OID_SHA256;
143 :
144 35 : SECOidData *od = SECOID_FindOIDByTag(mOidTagForStoringNewHashes);
145 35 : if (!od)
146 0 : return NS_ERROR_FAILURE;
147 :
148 35 : char *dotted_oid = CERT_GetOidString(&od->oid);
149 35 : if (!dotted_oid)
150 0 : return NS_ERROR_FAILURE;
151 :
152 35 : mDottedOidForStoringNewHashes = dotted_oid;
153 35 : PR_smprintf_free(dotted_oid);
154 :
155 : nsCOMPtr<nsIObserverService> observerService =
156 70 : mozilla::services::GetObserverService();
157 :
158 : // If we cannot add ourselves as a profile change observer, then we will not
159 : // attempt to read/write any settings file. Otherwise, we would end up
160 : // reading/writing the wrong settings file after a profile change.
161 35 : if (observerService) {
162 35 : observerService->AddObserver(this, "profile-before-change", true);
163 35 : observerService->AddObserver(this, "profile-do-change", true);
164 : // simulate a profile change so we read the current profile's settings file
165 35 : Observe(nsnull, "profile-do-change", nsnull);
166 : }
167 :
168 35 : return NS_OK;
169 : }
170 :
171 : NS_IMETHODIMP
172 65 : nsCertOverrideService::Observe(nsISupports *,
173 : const char *aTopic,
174 : const PRUnichar *aData)
175 : {
176 : // check the topic
177 65 : if (!nsCRT::strcmp(aTopic, "profile-before-change")) {
178 : // The profile is about to change,
179 : // or is going away because the application is shutting down.
180 :
181 60 : ReentrantMonitorAutoEnter lock(monitor);
182 :
183 30 : if (!nsCRT::strcmp(aData, NS_LITERAL_STRING("shutdown-cleanse").get())) {
184 0 : RemoveAllFromMemory();
185 : // delete the storage file
186 0 : if (mSettingsFile) {
187 0 : mSettingsFile->Remove(false);
188 : }
189 : } else {
190 30 : RemoveAllFromMemory();
191 : }
192 :
193 35 : } else if (!nsCRT::strcmp(aTopic, "profile-do-change")) {
194 : // The profile has already changed.
195 : // Now read from the new profile location.
196 : // we also need to update the cached file location
197 :
198 70 : ReentrantMonitorAutoEnter lock(monitor);
199 :
200 35 : nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mSettingsFile));
201 35 : if (NS_SUCCEEDED(rv)) {
202 32 : mSettingsFile->AppendNative(NS_LITERAL_CSTRING(kCertOverrideFileName));
203 : } else {
204 3 : mSettingsFile = nsnull;
205 : }
206 35 : Read();
207 :
208 : }
209 :
210 65 : return NS_OK;
211 : }
212 :
213 : void
214 30 : nsCertOverrideService::RemoveAllFromMemory()
215 : {
216 60 : ReentrantMonitorAutoEnter lock(monitor);
217 30 : mSettingsTable.Clear();
218 30 : }
219 :
220 : PR_STATIC_CALLBACK(PLDHashOperator)
221 0 : RemoveTemporariesCallback(nsCertOverrideEntry *aEntry,
222 : void *aArg)
223 : {
224 0 : if (aEntry && aEntry->mSettings.mIsTemporary) {
225 0 : aEntry->mSettings.mCert = nsnull;
226 0 : return PL_DHASH_REMOVE;
227 : }
228 :
229 0 : return PL_DHASH_NEXT;
230 : }
231 :
232 : void
233 142 : nsCertOverrideService::RemoveAllTemporaryOverrides()
234 : {
235 : {
236 284 : ReentrantMonitorAutoEnter lock(monitor);
237 142 : mSettingsTable.EnumerateEntries(RemoveTemporariesCallback, nsnull);
238 : // no need to write, as temporaries are never written to disk
239 : }
240 142 : }
241 :
242 : nsresult
243 35 : nsCertOverrideService::Read()
244 : {
245 70 : ReentrantMonitorAutoEnter lock(monitor);
246 :
247 : // If we don't have a profile, then we won't try to read any settings file.
248 35 : if (!mSettingsFile)
249 3 : return NS_OK;
250 :
251 : nsresult rv;
252 64 : nsCOMPtr<nsIInputStream> fileInputStream;
253 32 : rv = NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), mSettingsFile);
254 32 : if (NS_FAILED(rv)) {
255 32 : return rv;
256 : }
257 :
258 0 : nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(fileInputStream, &rv);
259 0 : if (NS_FAILED(rv)) {
260 0 : return rv;
261 : }
262 :
263 0 : nsCAutoString buffer;
264 0 : bool isMore = true;
265 0 : PRInt32 hostIndex = 0, algoIndex, fingerprintIndex, overrideBitsIndex, dbKeyIndex;
266 :
267 : /* file format is:
268 : *
269 : * host:port \t fingerprint-algorithm \t fingerprint \t override-mask \t dbKey
270 : *
271 : * where override-mask is a sequence of characters,
272 : * M meaning hostname-Mismatch-override
273 : * U meaning Untrusted-override
274 : * T meaning Time-error-override (expired/not yet valid)
275 : *
276 : * if this format isn't respected we move onto the next line in the file.
277 : */
278 :
279 0 : while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
280 0 : if (buffer.IsEmpty() || buffer.First() == '#') {
281 0 : continue;
282 : }
283 :
284 : // this is a cheap, cheesy way of parsing a tab-delimited line into
285 : // string indexes, which can be lopped off into substrings. just for
286 : // purposes of obfuscation, it also checks that each token was found.
287 : // todo: use iterators?
288 0 : if ((algoIndex = buffer.FindChar('\t', hostIndex) + 1) == 0 ||
289 0 : (fingerprintIndex = buffer.FindChar('\t', algoIndex) + 1) == 0 ||
290 0 : (overrideBitsIndex = buffer.FindChar('\t', fingerprintIndex) + 1) == 0 ||
291 0 : (dbKeyIndex = buffer.FindChar('\t', overrideBitsIndex) + 1) == 0) {
292 0 : continue;
293 : }
294 :
295 0 : const nsASingleFragmentCString &tmp = Substring(buffer, hostIndex, algoIndex - hostIndex - 1);
296 0 : const nsASingleFragmentCString &algo_string = Substring(buffer, algoIndex, fingerprintIndex - algoIndex - 1);
297 0 : const nsASingleFragmentCString &fingerprint = Substring(buffer, fingerprintIndex, overrideBitsIndex - fingerprintIndex - 1);
298 0 : const nsASingleFragmentCString &bits_string = Substring(buffer, overrideBitsIndex, dbKeyIndex - overrideBitsIndex - 1);
299 0 : const nsASingleFragmentCString &db_key = Substring(buffer, dbKeyIndex, buffer.Length() - dbKeyIndex);
300 :
301 0 : nsCAutoString host(tmp);
302 : nsCertOverride::OverrideBits bits;
303 0 : nsCertOverride::convertStringToBits(bits_string, bits);
304 :
305 : PRInt32 port;
306 0 : PRInt32 portIndex = host.RFindChar(':');
307 0 : if (portIndex == kNotFound)
308 0 : continue; // Ignore broken entries
309 :
310 : PRInt32 portParseError;
311 0 : nsCAutoString portString(Substring(host, portIndex+1));
312 0 : port = portString.ToInteger(&portParseError);
313 0 : if (portParseError)
314 0 : continue; // Ignore broken entries
315 :
316 0 : host.Truncate(portIndex);
317 :
318 : AddEntryToList(host, port,
319 : nsnull, // don't have the cert
320 : false, // not temporary
321 0 : algo_string, fingerprint, bits, db_key);
322 : }
323 :
324 0 : return NS_OK;
325 : }
326 :
327 : PR_STATIC_CALLBACK(PLDHashOperator)
328 0 : WriteEntryCallback(nsCertOverrideEntry *aEntry,
329 : void *aArg)
330 : {
331 : static const char kTab[] = "\t";
332 :
333 0 : nsIOutputStream *rawStreamPtr = (nsIOutputStream *)aArg;
334 :
335 : nsresult rv;
336 :
337 0 : if (rawStreamPtr && aEntry)
338 : {
339 0 : const nsCertOverride &settings = aEntry->mSettings;
340 0 : if (settings.mIsTemporary)
341 0 : return PL_DHASH_NEXT;
342 :
343 0 : nsCAutoString bits_string;
344 : nsCertOverride::convertBitsToString(settings.mOverrideBits,
345 0 : bits_string);
346 :
347 0 : rawStreamPtr->Write(aEntry->mHostWithPort.get(), aEntry->mHostWithPort.Length(), &rv);
348 0 : rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
349 : rawStreamPtr->Write(settings.mFingerprintAlgOID.get(),
350 0 : settings.mFingerprintAlgOID.Length(), &rv);
351 0 : rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
352 : rawStreamPtr->Write(settings.mFingerprint.get(),
353 0 : settings.mFingerprint.Length(), &rv);
354 0 : rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
355 : rawStreamPtr->Write(bits_string.get(),
356 0 : bits_string.Length(), &rv);
357 0 : rawStreamPtr->Write(kTab, sizeof(kTab) - 1, &rv);
358 0 : rawStreamPtr->Write(settings.mDBKey.get(), settings.mDBKey.Length(), &rv);
359 0 : rawStreamPtr->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &rv);
360 : }
361 :
362 0 : return PL_DHASH_NEXT;
363 : }
364 :
365 : nsresult
366 0 : nsCertOverrideService::Write()
367 : {
368 0 : ReentrantMonitorAutoEnter lock(monitor);
369 :
370 : // If we don't have any profile, then we won't try to write any file
371 0 : if (!mSettingsFile) {
372 0 : return NS_OK;
373 : }
374 :
375 : nsresult rv;
376 0 : nsCOMPtr<nsIOutputStream> fileOutputStream;
377 0 : rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream),
378 : mSettingsFile,
379 : -1,
380 0 : 0600);
381 0 : if (NS_FAILED(rv)) {
382 0 : NS_ERROR("failed to open cert_warn_settings.txt for writing");
383 0 : return rv;
384 : }
385 :
386 : // get a buffered output stream 4096 bytes big, to optimize writes
387 0 : nsCOMPtr<nsIOutputStream> bufferedOutputStream;
388 0 : rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), fileOutputStream, 4096);
389 0 : if (NS_FAILED(rv)) {
390 0 : return rv;
391 : }
392 :
393 : static const char kHeader[] =
394 : "# PSM Certificate Override Settings file" NS_LINEBREAK
395 : "# This is a generated file! Do not edit." NS_LINEBREAK;
396 :
397 : /* see ::Read for file format */
398 :
399 0 : bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &rv);
400 :
401 0 : nsIOutputStream *rawStreamPtr = bufferedOutputStream;
402 0 : mSettingsTable.EnumerateEntries(WriteEntryCallback, rawStreamPtr);
403 :
404 : // All went ok. Maybe except for problems in Write(), but the stream detects
405 : // that for us
406 0 : nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(bufferedOutputStream);
407 0 : NS_ASSERTION(safeStream, "expected a safe output stream!");
408 0 : if (safeStream) {
409 0 : rv = safeStream->Finish();
410 0 : if (NS_FAILED(rv)) {
411 0 : NS_WARNING("failed to save cert warn settings file! possible dataloss");
412 0 : return rv;
413 : }
414 : }
415 :
416 0 : return NS_OK;
417 : }
418 :
419 : static nsresult
420 0 : GetCertFingerprintByOidTag(CERTCertificate* nsscert,
421 : SECOidTag aOidTag,
422 : nsCString &fp)
423 : {
424 0 : unsigned int hash_len = HASH_ResultLenByOidTag(aOidTag);
425 0 : nsStringBuffer* fingerprint = nsStringBuffer::Alloc(hash_len);
426 0 : if (!fingerprint)
427 0 : return NS_ERROR_OUT_OF_MEMORY;
428 :
429 0 : PK11_HashBuf(aOidTag, (unsigned char*)fingerprint->Data(),
430 0 : nsscert->derCert.data, nsscert->derCert.len);
431 :
432 : SECItem fpItem;
433 0 : fpItem.data = (unsigned char*)fingerprint->Data();
434 0 : fpItem.len = hash_len;
435 :
436 0 : char *tmpstr = CERT_Hexify(&fpItem, 1);
437 0 : fp.Assign(tmpstr);
438 0 : PORT_Free(tmpstr);
439 0 : fingerprint->Release();
440 0 : return NS_OK;
441 : }
442 :
443 : static nsresult
444 0 : GetCertFingerprintByOidTag(nsIX509Cert *aCert,
445 : SECOidTag aOidTag,
446 : nsCString &fp)
447 : {
448 0 : nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
449 0 : if (!cert2)
450 0 : return NS_ERROR_FAILURE;
451 :
452 0 : CERTCertificate* nsscert = cert2->GetCert();
453 0 : if (!nsscert)
454 0 : return NS_ERROR_FAILURE;
455 :
456 0 : CERTCertificateCleaner nsscertCleaner(nsscert);
457 0 : return GetCertFingerprintByOidTag(nsscert, aOidTag, fp);
458 : }
459 :
460 : static nsresult
461 0 : GetCertFingerprintByDottedOidString(CERTCertificate* nsscert,
462 : const nsCString &dottedOid,
463 : nsCString &fp)
464 : {
465 : SECItem oid;
466 0 : oid.data = nsnull;
467 0 : oid.len = 0;
468 : SECStatus srv = SEC_StringToOID(nsnull, &oid,
469 0 : dottedOid.get(), dottedOid.Length());
470 0 : if (srv != SECSuccess)
471 0 : return NS_ERROR_FAILURE;
472 :
473 0 : SECOidTag oid_tag = SECOID_FindOIDTag(&oid);
474 0 : SECITEM_FreeItem(&oid, false);
475 :
476 0 : if (oid_tag == SEC_OID_UNKNOWN)
477 0 : return NS_ERROR_FAILURE;
478 :
479 0 : return GetCertFingerprintByOidTag(nsscert, oid_tag, fp);
480 : }
481 :
482 : static nsresult
483 0 : GetCertFingerprintByDottedOidString(nsIX509Cert *aCert,
484 : const nsCString &dottedOid,
485 : nsCString &fp)
486 : {
487 0 : nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
488 0 : if (!cert2)
489 0 : return NS_ERROR_FAILURE;
490 :
491 0 : CERTCertificate* nsscert = cert2->GetCert();
492 0 : if (!nsscert)
493 0 : return NS_ERROR_FAILURE;
494 :
495 0 : CERTCertificateCleaner nsscertCleaner(nsscert);
496 0 : return GetCertFingerprintByDottedOidString(nsscert, dottedOid, fp);
497 : }
498 :
499 : NS_IMETHODIMP
500 0 : nsCertOverrideService::RememberValidityOverride(const nsACString & aHostName, PRInt32 aPort,
501 : nsIX509Cert *aCert,
502 : PRUint32 aOverrideBits,
503 : bool aTemporary)
504 : {
505 0 : NS_ENSURE_ARG_POINTER(aCert);
506 0 : if (aHostName.IsEmpty())
507 0 : return NS_ERROR_INVALID_ARG;
508 0 : if (aPort < -1)
509 0 : return NS_ERROR_INVALID_ARG;
510 :
511 0 : nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(aCert);
512 0 : if (!cert2)
513 0 : return NS_ERROR_FAILURE;
514 :
515 0 : CERTCertificate* nsscert = cert2->GetCert();
516 0 : if (!nsscert)
517 0 : return NS_ERROR_FAILURE;
518 :
519 0 : CERTCertificateCleaner nsscertCleaner(nsscert);
520 :
521 0 : char* nickname = nsNSSCertificate::defaultServerNickname(nsscert);
522 0 : if (!aTemporary && nickname && *nickname)
523 : {
524 0 : PK11SlotInfo *slot = PK11_GetInternalKeySlot();
525 0 : if (!slot) {
526 0 : PR_Free(nickname);
527 0 : return NS_ERROR_FAILURE;
528 : }
529 :
530 : SECStatus srv = PK11_ImportCert(slot, nsscert, CK_INVALID_HANDLE,
531 0 : nickname, false);
532 0 : PK11_FreeSlot(slot);
533 :
534 0 : if (srv != SECSuccess) {
535 0 : PR_Free(nickname);
536 0 : return NS_ERROR_FAILURE;
537 : }
538 : }
539 0 : PR_FREEIF(nickname);
540 :
541 0 : nsCAutoString fpStr;
542 : nsresult rv = GetCertFingerprintByOidTag(nsscert,
543 0 : mOidTagForStoringNewHashes, fpStr);
544 0 : if (NS_FAILED(rv))
545 0 : return rv;
546 :
547 0 : char *dbkey = NULL;
548 0 : rv = aCert->GetDbKey(&dbkey);
549 0 : if (NS_FAILED(rv) || !dbkey)
550 0 : return rv;
551 :
552 : // change \n and \r to spaces in the possibly multi-line-base64-encoded key
553 0 : for (char *dbkey_walk = dbkey;
554 : *dbkey_walk;
555 : ++dbkey_walk) {
556 0 : char c = *dbkey_walk;
557 0 : if (c == '\r' || c == '\n') {
558 0 : *dbkey_walk = ' ';
559 : }
560 : }
561 :
562 : {
563 0 : ReentrantMonitorAutoEnter lock(monitor);
564 : AddEntryToList(aHostName, aPort,
565 : aTemporary ? aCert : nsnull,
566 : // keep a reference to the cert for temporary overrides
567 : aTemporary,
568 : mDottedOidForStoringNewHashes, fpStr,
569 : (nsCertOverride::OverrideBits)aOverrideBits,
570 0 : nsDependentCString(dbkey));
571 0 : Write();
572 : }
573 :
574 0 : PR_Free(dbkey);
575 0 : return NS_OK;
576 : }
577 :
578 : NS_IMETHODIMP
579 0 : nsCertOverrideService::HasMatchingOverride(const nsACString & aHostName, PRInt32 aPort,
580 : nsIX509Cert *aCert,
581 : PRUint32 *aOverrideBits,
582 : bool *aIsTemporary,
583 : bool *_retval)
584 : {
585 0 : if (aHostName.IsEmpty())
586 0 : return NS_ERROR_INVALID_ARG;
587 0 : if (aPort < -1)
588 0 : return NS_ERROR_INVALID_ARG;
589 :
590 0 : NS_ENSURE_ARG_POINTER(aCert);
591 0 : NS_ENSURE_ARG_POINTER(aOverrideBits);
592 0 : NS_ENSURE_ARG_POINTER(aIsTemporary);
593 0 : NS_ENSURE_ARG_POINTER(_retval);
594 0 : *_retval = false;
595 0 : *aOverrideBits = nsCertOverride::ob_None;
596 :
597 0 : nsCAutoString hostPort;
598 0 : GetHostWithPort(aHostName, aPort, hostPort);
599 0 : nsCertOverride settings;
600 :
601 : {
602 0 : ReentrantMonitorAutoEnter lock(monitor);
603 0 : nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get());
604 :
605 0 : if (!entry)
606 0 : return NS_OK;
607 :
608 0 : settings = entry->mSettings; // copy
609 : }
610 :
611 0 : *aOverrideBits = settings.mOverrideBits;
612 0 : *aIsTemporary = settings.mIsTemporary;
613 :
614 0 : nsCAutoString fpStr;
615 : nsresult rv;
616 :
617 0 : if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
618 0 : rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr);
619 : }
620 : else {
621 0 : rv = GetCertFingerprintByDottedOidString(aCert, settings.mFingerprintAlgOID, fpStr);
622 : }
623 0 : if (NS_FAILED(rv))
624 0 : return rv;
625 :
626 0 : *_retval = settings.mFingerprint.Equals(fpStr);
627 0 : return NS_OK;
628 : }
629 :
630 : NS_IMETHODIMP
631 0 : nsCertOverrideService::GetValidityOverride(const nsACString & aHostName, PRInt32 aPort,
632 : nsACString & aHashAlg,
633 : nsACString & aFingerprint,
634 : PRUint32 *aOverrideBits,
635 : bool *aIsTemporary,
636 : bool *_found)
637 : {
638 0 : NS_ENSURE_ARG_POINTER(_found);
639 0 : NS_ENSURE_ARG_POINTER(aIsTemporary);
640 0 : NS_ENSURE_ARG_POINTER(aOverrideBits);
641 0 : *_found = false;
642 0 : *aOverrideBits = nsCertOverride::ob_None;
643 :
644 0 : nsCAutoString hostPort;
645 0 : GetHostWithPort(aHostName, aPort, hostPort);
646 0 : nsCertOverride settings;
647 :
648 : {
649 0 : ReentrantMonitorAutoEnter lock(monitor);
650 0 : nsCertOverrideEntry *entry = mSettingsTable.GetEntry(hostPort.get());
651 :
652 0 : if (entry) {
653 0 : *_found = true;
654 0 : settings = entry->mSettings; // copy
655 : }
656 : }
657 :
658 0 : if (*_found) {
659 0 : *aOverrideBits = settings.mOverrideBits;
660 0 : *aIsTemporary = settings.mIsTemporary;
661 0 : aFingerprint = settings.mFingerprint;
662 0 : aHashAlg = settings.mFingerprintAlgOID;
663 : }
664 :
665 0 : return NS_OK;
666 : }
667 :
668 : nsresult
669 0 : nsCertOverrideService::AddEntryToList(const nsACString &aHostName, PRInt32 aPort,
670 : nsIX509Cert *aCert,
671 : const bool aIsTemporary,
672 : const nsACString &fingerprintAlgOID,
673 : const nsACString &fingerprint,
674 : nsCertOverride::OverrideBits ob,
675 : const nsACString &dbKey)
676 : {
677 0 : nsCAutoString hostPort;
678 0 : GetHostWithPort(aHostName, aPort, hostPort);
679 :
680 : {
681 0 : ReentrantMonitorAutoEnter lock(monitor);
682 0 : nsCertOverrideEntry *entry = mSettingsTable.PutEntry(hostPort.get());
683 :
684 0 : if (!entry) {
685 0 : NS_ERROR("can't insert a null entry!");
686 0 : return NS_ERROR_OUT_OF_MEMORY;
687 : }
688 :
689 0 : entry->mHostWithPort = hostPort;
690 :
691 0 : nsCertOverride &settings = entry->mSettings;
692 0 : settings.mAsciiHost = aHostName;
693 0 : settings.mPort = aPort;
694 0 : settings.mIsTemporary = aIsTemporary;
695 0 : settings.mFingerprintAlgOID = fingerprintAlgOID;
696 0 : settings.mFingerprint = fingerprint;
697 0 : settings.mOverrideBits = ob;
698 0 : settings.mDBKey = dbKey;
699 0 : settings.mCert = aCert;
700 : }
701 :
702 0 : return NS_OK;
703 : }
704 :
705 : NS_IMETHODIMP
706 142 : nsCertOverrideService::ClearValidityOverride(const nsACString & aHostName, PRInt32 aPort)
707 : {
708 284 : if (aPort == 0 &&
709 142 : aHostName.EqualsLiteral("all:temporary-certificates")) {
710 142 : RemoveAllTemporaryOverrides();
711 142 : return NS_OK;
712 : }
713 0 : nsCAutoString hostPort;
714 0 : GetHostWithPort(aHostName, aPort, hostPort);
715 : {
716 0 : ReentrantMonitorAutoEnter lock(monitor);
717 0 : mSettingsTable.RemoveEntry(hostPort.get());
718 0 : Write();
719 : }
720 0 : SSL_ClearSessionCache();
721 0 : return NS_OK;
722 : }
723 :
724 : NS_IMETHODIMP
725 0 : nsCertOverrideService::GetAllOverrideHostsWithPorts(PRUint32 *aCount,
726 : PRUnichar ***aHostsWithPortsArray)
727 : {
728 0 : return NS_ERROR_NOT_IMPLEMENTED;
729 : }
730 :
731 : static bool
732 0 : matchesDBKey(nsIX509Cert *cert, const char *match_dbkey)
733 : {
734 0 : char *dbkey = NULL;
735 0 : nsresult rv = cert->GetDbKey(&dbkey);
736 0 : if (NS_FAILED(rv) || !dbkey)
737 0 : return false;
738 :
739 0 : bool found_mismatch = false;
740 0 : const char *key1 = dbkey;
741 0 : const char *key2 = match_dbkey;
742 :
743 : // skip over any whitespace when comparing
744 0 : while (*key1 && *key2) {
745 0 : char c1 = *key1;
746 0 : char c2 = *key2;
747 :
748 0 : switch (c1) {
749 : case ' ':
750 : case '\t':
751 : case '\n':
752 : case '\r':
753 0 : ++key1;
754 0 : continue;
755 : }
756 :
757 0 : switch (c2) {
758 : case ' ':
759 : case '\t':
760 : case '\n':
761 : case '\r':
762 0 : ++key2;
763 0 : continue;
764 : }
765 :
766 0 : if (c1 != c2) {
767 0 : found_mismatch = true;
768 0 : break;
769 : }
770 :
771 0 : ++key1;
772 0 : ++key2;
773 : }
774 :
775 0 : PR_Free(dbkey);
776 0 : return !found_mismatch;
777 : }
778 :
779 : struct nsCertAndBoolsAndInt
780 0 : {
781 : nsIX509Cert *cert;
782 : bool aCheckTemporaries;
783 : bool aCheckPermanents;
784 : PRUint32 counter;
785 :
786 : SECOidTag mOidTagForStoringNewHashes;
787 : nsCString mDottedOidForStoringNewHashes;
788 : };
789 :
790 : PR_STATIC_CALLBACK(PLDHashOperator)
791 0 : FindMatchingCertCallback(nsCertOverrideEntry *aEntry,
792 : void *aArg)
793 : {
794 0 : nsCertAndBoolsAndInt *cai = (nsCertAndBoolsAndInt *)aArg;
795 :
796 0 : if (cai && aEntry)
797 : {
798 0 : const nsCertOverride &settings = aEntry->mSettings;
799 0 : bool still_ok = true;
800 :
801 0 : if ((settings.mIsTemporary && !cai->aCheckTemporaries)
802 : ||
803 0 : (!settings.mIsTemporary && !cai->aCheckPermanents)) {
804 0 : still_ok = false;
805 : }
806 :
807 0 : if (still_ok && matchesDBKey(cai->cert, settings.mDBKey.get())) {
808 0 : nsCAutoString cert_fingerprint;
809 : nsresult rv;
810 0 : if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) {
811 : rv = GetCertFingerprintByOidTag(cai->cert,
812 0 : cai->mOidTagForStoringNewHashes, cert_fingerprint);
813 : }
814 : else {
815 : rv = GetCertFingerprintByDottedOidString(cai->cert,
816 0 : settings.mFingerprintAlgOID, cert_fingerprint);
817 : }
818 0 : if (NS_SUCCEEDED(rv) &&
819 0 : settings.mFingerprint.Equals(cert_fingerprint)) {
820 0 : cai->counter++;
821 : }
822 : }
823 : }
824 :
825 0 : return PL_DHASH_NEXT;
826 : }
827 :
828 : NS_IMETHODIMP
829 0 : nsCertOverrideService::IsCertUsedForOverrides(nsIX509Cert *aCert,
830 : bool aCheckTemporaries,
831 : bool aCheckPermanents,
832 : PRUint32 *_retval)
833 : {
834 0 : NS_ENSURE_ARG(aCert);
835 0 : NS_ENSURE_ARG(_retval);
836 :
837 0 : nsCertAndBoolsAndInt cai;
838 0 : cai.cert = aCert;
839 0 : cai.aCheckTemporaries = aCheckTemporaries;
840 0 : cai.aCheckPermanents = aCheckPermanents;
841 0 : cai.counter = 0;
842 0 : cai.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
843 0 : cai.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
844 :
845 : {
846 0 : ReentrantMonitorAutoEnter lock(monitor);
847 0 : mSettingsTable.EnumerateEntries(FindMatchingCertCallback, &cai);
848 : }
849 0 : *_retval = cai.counter;
850 0 : return NS_OK;
851 : }
852 :
853 : struct nsCertAndPointerAndCallback
854 0 : {
855 : nsIX509Cert *cert;
856 : void *userdata;
857 : nsCertOverrideService::CertOverrideEnumerator enumerator;
858 :
859 : SECOidTag mOidTagForStoringNewHashes;
860 : nsCString mDottedOidForStoringNewHashes;
861 : };
862 :
863 : PR_STATIC_CALLBACK(PLDHashOperator)
864 0 : EnumerateCertOverridesCallback(nsCertOverrideEntry *aEntry,
865 : void *aArg)
866 : {
867 0 : nsCertAndPointerAndCallback *capac = (nsCertAndPointerAndCallback *)aArg;
868 :
869 0 : if (capac && aEntry)
870 : {
871 0 : const nsCertOverride &settings = aEntry->mSettings;
872 :
873 0 : if (!capac->cert) {
874 0 : (*capac->enumerator)(settings, capac->userdata);
875 : }
876 : else {
877 0 : if (matchesDBKey(capac->cert, settings.mDBKey.get())) {
878 0 : nsCAutoString cert_fingerprint;
879 : nsresult rv;
880 0 : if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) {
881 : rv = GetCertFingerprintByOidTag(capac->cert,
882 0 : capac->mOidTagForStoringNewHashes, cert_fingerprint);
883 : }
884 : else {
885 : rv = GetCertFingerprintByDottedOidString(capac->cert,
886 0 : settings.mFingerprintAlgOID, cert_fingerprint);
887 : }
888 0 : if (NS_SUCCEEDED(rv) &&
889 0 : settings.mFingerprint.Equals(cert_fingerprint)) {
890 0 : (*capac->enumerator)(settings, capac->userdata);
891 : }
892 : }
893 : }
894 : }
895 :
896 0 : return PL_DHASH_NEXT;
897 : }
898 :
899 : nsresult
900 0 : nsCertOverrideService::EnumerateCertOverrides(nsIX509Cert *aCert,
901 : CertOverrideEnumerator enumerator,
902 : void *aUserData)
903 : {
904 0 : nsCertAndPointerAndCallback capac;
905 0 : capac.cert = aCert;
906 0 : capac.userdata = aUserData;
907 0 : capac.enumerator = enumerator;
908 0 : capac.mOidTagForStoringNewHashes = mOidTagForStoringNewHashes;
909 0 : capac.mDottedOidForStoringNewHashes = mDottedOidForStoringNewHashes;
910 :
911 : {
912 0 : ReentrantMonitorAutoEnter lock(monitor);
913 0 : mSettingsTable.EnumerateEntries(EnumerateCertOverridesCallback, &capac);
914 : }
915 0 : return NS_OK;
916 : }
917 :
918 : void
919 0 : nsCertOverrideService::GetHostWithPort(const nsACString & aHostName, PRInt32 aPort, nsACString& _retval)
920 : {
921 0 : nsCAutoString hostPort(aHostName);
922 0 : if (aPort == -1) {
923 0 : aPort = 443;
924 : }
925 0 : if (!hostPort.IsEmpty()) {
926 0 : hostPort.AppendLiteral(":");
927 0 : hostPort.AppendInt(aPort);
928 : }
929 0 : _retval.Assign(hostPort);
930 0 : }
931 :
|