Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "TransportSecurityInfo.h"
9 #include "pkix/pkixtypes.h"
10 #include "nsNSSComponent.h"
11 #include "nsIWebProgressListener.h"
12 #include "nsNSSCertificate.h"
13 #include "nsIX509CertValidity.h"
14 #include "nsIDateTimeFormat.h"
15 #include "nsDateTimeFormatCID.h"
16 #include "nsICertOverrideService.h"
17 #include "nsIObjectInputStream.h"
18 #include "nsIObjectOutputStream.h"
19 #include "nsNSSCertHelper.h"
20 #include "nsIProgrammingLanguage.h"
21 #include "nsIArray.h"
22 #include "nsComponentManagerUtils.h"
23 #include "nsReadableUtils.h"
24 #include "nsServiceManagerUtils.h"
25 #include "PSMRunnable.h"
27 #include "secerr.h"
29 //#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
30 //reports when doing SSL read/write
32 //#define DUMP_BUFFER //Enable this define along with
33 //DEBUG_SSL_VERBOSE to dump SSL
34 //read/write buffer to a log.
35 //Uses PR_LOG except on Mac where
36 //we always write out to our own
37 //file.
39 namespace mozilla { namespace psm {
41 TransportSecurityInfo::TransportSecurityInfo()
42 : mMutex("TransportSecurityInfo::mMutex"),
43 mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE),
44 mSubRequestsBrokenSecurity(0),
45 mSubRequestsNoSecurity(0),
46 mErrorCode(0),
47 mErrorMessageType(PlainErrorMessage),
48 mPort(0)
49 {
50 }
52 TransportSecurityInfo::~TransportSecurityInfo()
53 {
54 nsNSSShutDownPreventionLock locker;
55 if (isAlreadyShutDown())
56 return;
58 shutdown(calledFromObject);
59 }
61 void
62 TransportSecurityInfo::virtualDestroyNSSReference()
63 {
64 }
66 NS_IMPL_ISUPPORTS(TransportSecurityInfo,
67 nsITransportSecurityInfo,
68 nsIInterfaceRequestor,
69 nsISSLStatusProvider,
70 nsIAssociatedContentSecurity,
71 nsISerializable,
72 nsIClassInfo)
74 nsresult
75 TransportSecurityInfo::SetHostName(const char* host)
76 {
77 mHostName.Adopt(host ? NS_strdup(host) : 0);
78 return NS_OK;
79 }
81 nsresult
82 TransportSecurityInfo::GetHostName(char **host)
83 {
84 *host = (mHostName) ? NS_strdup(mHostName) : nullptr;
85 return NS_OK;
86 }
88 nsresult
89 TransportSecurityInfo::SetPort(int32_t aPort)
90 {
91 mPort = aPort;
92 return NS_OK;
93 }
95 nsresult
96 TransportSecurityInfo::GetPort(int32_t *aPort)
97 {
98 *aPort = mPort;
99 return NS_OK;
100 }
102 PRErrorCode
103 TransportSecurityInfo::GetErrorCode() const
104 {
105 MutexAutoLock lock(mMutex);
107 return mErrorCode;
108 }
110 void
111 TransportSecurityInfo::SetCanceled(PRErrorCode errorCode,
112 SSLErrorMessageType errorMessageType)
113 {
114 MutexAutoLock lock(mMutex);
116 mErrorCode = errorCode;
117 mErrorMessageType = errorMessageType;
118 mErrorMessageCached.Truncate();
119 }
121 NS_IMETHODIMP
122 TransportSecurityInfo::GetSecurityState(uint32_t* state)
123 {
124 *state = mSecurityState;
125 return NS_OK;
126 }
128 nsresult
129 TransportSecurityInfo::SetSecurityState(uint32_t aState)
130 {
131 mSecurityState = aState;
132 return NS_OK;
133 }
135 /* attribute unsigned long countSubRequestsBrokenSecurity; */
136 NS_IMETHODIMP
137 TransportSecurityInfo::GetCountSubRequestsBrokenSecurity(
138 int32_t *aSubRequestsBrokenSecurity)
139 {
140 *aSubRequestsBrokenSecurity = mSubRequestsBrokenSecurity;
141 return NS_OK;
142 }
144 NS_IMETHODIMP
145 TransportSecurityInfo::SetCountSubRequestsBrokenSecurity(
146 int32_t aSubRequestsBrokenSecurity)
147 {
148 mSubRequestsBrokenSecurity = aSubRequestsBrokenSecurity;
149 return NS_OK;
150 }
152 /* attribute unsigned long countSubRequestsNoSecurity; */
153 NS_IMETHODIMP
154 TransportSecurityInfo::GetCountSubRequestsNoSecurity(
155 int32_t *aSubRequestsNoSecurity)
156 {
157 *aSubRequestsNoSecurity = mSubRequestsNoSecurity;
158 return NS_OK;
159 }
161 NS_IMETHODIMP
162 TransportSecurityInfo::SetCountSubRequestsNoSecurity(
163 int32_t aSubRequestsNoSecurity)
164 {
165 mSubRequestsNoSecurity = aSubRequestsNoSecurity;
166 return NS_OK;
167 }
169 NS_IMETHODIMP
170 TransportSecurityInfo::Flush()
171 {
172 return NS_OK;
173 }
175 NS_IMETHODIMP
176 TransportSecurityInfo::GetErrorMessage(char16_t** aText)
177 {
178 NS_ENSURE_ARG_POINTER(aText);
179 *aText = nullptr;
181 if (!NS_IsMainThread()) {
182 NS_ERROR("nsNSSSocketInfo::GetErrorMessage called off the main thread");
183 return NS_ERROR_NOT_SAME_THREAD;
184 }
186 MutexAutoLock lock(mMutex);
188 if (mErrorMessageCached.IsEmpty()) {
189 nsresult rv = formatErrorMessage(lock,
190 mErrorCode, mErrorMessageType,
191 true, true, mErrorMessageCached);
192 NS_ENSURE_SUCCESS(rv, rv);
193 }
195 *aText = ToNewUnicode(mErrorMessageCached);
196 return *aText ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
197 }
199 void
200 TransportSecurityInfo::GetErrorLogMessage(PRErrorCode errorCode,
201 SSLErrorMessageType errorMessageType,
202 nsString &result)
203 {
204 if (!NS_IsMainThread()) {
205 NS_ERROR("nsNSSSocketInfo::GetErrorLogMessage called off the main thread");
206 return;
207 }
209 MutexAutoLock lock(mMutex);
210 (void) formatErrorMessage(lock, errorCode, errorMessageType,
211 false, false, result);
212 }
214 static nsresult
215 formatPlainErrorMessage(nsXPIDLCString const & host, int32_t port,
216 PRErrorCode err,
217 bool suppressPort443,
218 nsString &returnedMessage);
220 static nsresult
221 formatOverridableCertErrorMessage(nsISSLStatus & sslStatus,
222 PRErrorCode errorCodeToReport,
223 const nsXPIDLCString & host, int32_t port,
224 bool suppressPort443,
225 bool wantsHtml,
226 nsString & returnedMessage);
228 // XXX: uses nsNSSComponent string bundles off the main thread when called by
229 // nsNSSSocketInfo::Write().
230 nsresult
231 TransportSecurityInfo::formatErrorMessage(MutexAutoLock const & proofOfLock,
232 PRErrorCode errorCode,
233 SSLErrorMessageType errorMessageType,
234 bool wantsHtml, bool suppressPort443,
235 nsString &result)
236 {
237 if (errorCode == 0) {
238 result.Truncate();
239 return NS_OK;
240 }
242 nsresult rv;
243 NS_ConvertASCIItoUTF16 hostNameU(mHostName);
244 NS_ASSERTION(errorMessageType != OverridableCertErrorMessage ||
245 (mSSLStatus && mSSLStatus->mServerCert &&
246 mSSLStatus->mHaveCertErrorBits),
247 "GetErrorLogMessage called for cert error without cert");
248 if (errorMessageType == OverridableCertErrorMessage &&
249 mSSLStatus && mSSLStatus->mServerCert) {
250 rv = formatOverridableCertErrorMessage(*mSSLStatus, errorCode,
251 mHostName, mPort,
252 suppressPort443,
253 wantsHtml,
254 result);
255 } else {
256 rv = formatPlainErrorMessage(mHostName, mPort,
257 errorCode,
258 suppressPort443,
259 result);
260 }
262 if (NS_FAILED(rv)) {
263 result.Truncate();
264 }
266 return rv;
267 }
269 /* void getInterface (in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */
270 NS_IMETHODIMP
271 TransportSecurityInfo::GetInterface(const nsIID & uuid, void * *result)
272 {
273 if (!NS_IsMainThread()) {
274 NS_ERROR("nsNSSSocketInfo::GetInterface called off the main thread");
275 return NS_ERROR_NOT_SAME_THREAD;
276 }
278 nsresult rv;
279 if (!mCallbacks) {
280 nsCOMPtr<nsIInterfaceRequestor> ir = new PipUIContext();
281 rv = ir->GetInterface(uuid, result);
282 } else {
283 rv = mCallbacks->GetInterface(uuid, result);
284 }
285 return rv;
286 }
288 // This is a new magic value. However, it re-uses the first 4 bytes
289 // of the previous value. This is so when older versions attempt to
290 // read a newer serialized TransportSecurityInfo, they will actually
291 // fail and return NS_ERROR_FAILURE instead of silently failing.
292 #define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0x28ea, 0x45d2, \
293 { 0xa2, 0x5a, 0x35, 0x7c, 0xae, 0xfa, 0x7f, 0x82 } }
294 static NS_DEFINE_CID(kTransportSecurityInfoMagic, TRANSPORTSECURITYINFOMAGIC);
296 NS_IMETHODIMP
297 TransportSecurityInfo::Write(nsIObjectOutputStream* stream)
298 {
299 nsresult rv = stream->WriteID(kTransportSecurityInfoMagic);
300 if (NS_FAILED(rv)) {
301 return rv;
302 }
304 MutexAutoLock lock(mMutex);
306 rv = stream->Write32(mSecurityState);
307 if (NS_FAILED(rv)) {
308 return rv;
309 }
310 rv = stream->Write32(mSubRequestsBrokenSecurity);
311 if (NS_FAILED(rv)) {
312 return rv;
313 }
314 rv = stream->Write32(mSubRequestsNoSecurity);
315 if (NS_FAILED(rv)) {
316 return rv;
317 }
318 // XXX: uses nsNSSComponent string bundles off the main thread
319 rv = formatErrorMessage(lock, mErrorCode, mErrorMessageType, true, true,
320 mErrorMessageCached);
321 if (NS_FAILED(rv)) {
322 return rv;
323 }
324 rv = stream->WriteWStringZ(mErrorMessageCached.get());
325 if (NS_FAILED(rv)) {
326 return rv;
327 }
328 nsCOMPtr<nsISerializable> serializable(mSSLStatus);
329 rv = stream->WriteCompoundObject(serializable, NS_GET_IID(nsISSLStatus),
330 true);
331 if (NS_FAILED(rv)) {
332 return rv;
333 }
334 return NS_OK;
335 }
337 NS_IMETHODIMP
338 TransportSecurityInfo::Read(nsIObjectInputStream* stream)
339 {
340 nsID id;
341 nsresult rv = stream->ReadID(&id);
342 if (NS_FAILED(rv)) {
343 return rv;
344 }
345 if (!id.Equals(kTransportSecurityInfoMagic)) {
346 return NS_ERROR_UNEXPECTED;
347 }
349 MutexAutoLock lock(mMutex);
351 rv = stream->Read32(&mSecurityState);
352 if (NS_FAILED(rv)) {
353 return rv;
354 }
355 uint32_t subRequestsBrokenSecurity;
356 rv = stream->Read32(&subRequestsBrokenSecurity);
357 if (NS_FAILED(rv)) {
358 return rv;
359 }
360 if (subRequestsBrokenSecurity >
361 static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
362 return NS_ERROR_UNEXPECTED;
363 }
364 mSubRequestsBrokenSecurity = subRequestsBrokenSecurity;
365 uint32_t subRequestsNoSecurity;
366 rv = stream->Read32(&subRequestsNoSecurity);
367 if (NS_FAILED(rv)) {
368 return rv;
369 }
370 if (subRequestsNoSecurity >
371 static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
372 return NS_ERROR_UNEXPECTED;
373 }
374 mSubRequestsNoSecurity = subRequestsNoSecurity;
375 rv = stream->ReadString(mErrorMessageCached);
376 if (NS_FAILED(rv)) {
377 return rv;
378 }
379 mErrorCode = 0;
380 nsCOMPtr<nsISupports> supports;
381 rv = stream->ReadObject(true, getter_AddRefs(supports));
382 if (NS_FAILED(rv)) {
383 return rv;
384 }
385 mSSLStatus = reinterpret_cast<nsSSLStatus*>(supports.get());
386 if (!mSSLStatus) {
387 return NS_ERROR_FAILURE;
388 }
389 return NS_OK;
390 }
392 NS_IMETHODIMP
393 TransportSecurityInfo::GetInterfaces(uint32_t *count, nsIID * **array)
394 {
395 *count = 0;
396 *array = nullptr;
397 return NS_OK;
398 }
400 NS_IMETHODIMP
401 TransportSecurityInfo::GetHelperForLanguage(uint32_t language,
402 nsISupports **_retval)
403 {
404 *_retval = nullptr;
405 return NS_OK;
406 }
408 NS_IMETHODIMP
409 TransportSecurityInfo::GetContractID(char * *aContractID)
410 {
411 *aContractID = nullptr;
412 return NS_OK;
413 }
415 NS_IMETHODIMP
416 TransportSecurityInfo::GetClassDescription(char * *aClassDescription)
417 {
418 *aClassDescription = nullptr;
419 return NS_OK;
420 }
422 NS_IMETHODIMP
423 TransportSecurityInfo::GetClassID(nsCID * *aClassID)
424 {
425 *aClassID = (nsCID*) nsMemory::Alloc(sizeof(nsCID));
426 if (!*aClassID)
427 return NS_ERROR_OUT_OF_MEMORY;
428 return GetClassIDNoAlloc(*aClassID);
429 }
431 NS_IMETHODIMP
432 TransportSecurityInfo::GetImplementationLanguage(
433 uint32_t *aImplementationLanguage)
434 {
435 *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
436 return NS_OK;
437 }
439 NS_IMETHODIMP
440 TransportSecurityInfo::GetFlags(uint32_t *aFlags)
441 {
442 *aFlags = 0;
443 return NS_OK;
444 }
446 static NS_DEFINE_CID(kNSSSocketInfoCID, TRANSPORTSECURITYINFO_CID);
448 NS_IMETHODIMP
449 TransportSecurityInfo::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
450 {
451 *aClassIDNoAlloc = kNSSSocketInfoCID;
452 return NS_OK;
453 }
455 nsresult
456 TransportSecurityInfo::GetSSLStatus(nsISSLStatus** _result)
457 {
458 NS_ENSURE_ARG_POINTER(_result);
460 *_result = mSSLStatus;
461 NS_IF_ADDREF(*_result);
463 return NS_OK;
464 }
466 nsresult
467 TransportSecurityInfo::SetSSLStatus(nsSSLStatus *aSSLStatus)
468 {
469 mSSLStatus = aSSLStatus;
471 return NS_OK;
472 }
474 /* Formats an error message for non-certificate-related SSL errors
475 * and non-overridable certificate errors (both are of type
476 * PlainErrormMessage). Use formatOverridableCertErrorMessage
477 * for overridable cert errors.
478 */
479 static nsresult
480 formatPlainErrorMessage(const nsXPIDLCString &host, int32_t port,
481 PRErrorCode err,
482 bool suppressPort443,
483 nsString &returnedMessage)
484 {
485 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
487 const char16_t *params[1];
488 nsresult rv;
490 nsCOMPtr<nsINSSComponent> component = do_GetService(kNSSComponentCID, &rv);
491 NS_ENSURE_SUCCESS(rv, rv);
493 if (host.Length())
494 {
495 nsString hostWithPort;
497 // For now, hide port when it's 443 and we're reporting the error.
498 // In the future a better mechanism should be used
499 // to make a decision about showing the port number, possibly by requiring
500 // the context object to implement a specific interface.
501 // The motivation is that Mozilla browser would like to hide the port number
502 // in error pages in the common case.
504 hostWithPort.AssignASCII(host);
505 if (!suppressPort443 || port != 443) {
506 hostWithPort.AppendLiteral(":");
507 hostWithPort.AppendInt(port);
508 }
509 params[0] = hostWithPort.get();
511 nsString formattedString;
512 rv = component->PIPBundleFormatStringFromName("SSLConnectionErrorPrefix",
513 params, 1,
514 formattedString);
515 if (NS_SUCCEEDED(rv))
516 {
517 returnedMessage.Append(formattedString);
518 returnedMessage.Append(NS_LITERAL_STRING("\n\n"));
519 }
520 }
522 nsString explanation;
523 rv = nsNSSErrors::getErrorMessageFromCode(err, component, explanation);
524 if (NS_SUCCEEDED(rv))
525 returnedMessage.Append(explanation);
527 return NS_OK;
528 }
530 static void
531 AppendErrorTextUntrusted(PRErrorCode errTrust,
532 const nsString &host,
533 nsIX509Cert* ix509,
534 nsINSSComponent *component,
535 nsString &returnedMessage)
536 {
537 const char *errorID = nullptr;
538 nsCOMPtr<nsIX509Cert3> cert3 = do_QueryInterface(ix509);
539 if (cert3) {
540 bool isSelfSigned;
541 if (NS_SUCCEEDED(cert3->GetIsSelfSigned(&isSelfSigned))
542 && isSelfSigned) {
543 errorID = "certErrorTrust_SelfSigned";
544 }
545 }
547 if (!errorID) {
548 switch (errTrust) {
549 case SEC_ERROR_UNKNOWN_ISSUER:
550 {
551 nsCOMPtr<nsIArray> chain;
552 ix509->GetChain(getter_AddRefs(chain));
553 uint32_t length = 0;
554 if (chain && NS_FAILED(chain->GetLength(&length)))
555 length = 0;
556 if (length == 1)
557 errorID = "certErrorTrust_MissingChain";
558 else
559 errorID = "certErrorTrust_UnknownIssuer";
560 break;
561 }
562 case SEC_ERROR_CA_CERT_INVALID:
563 errorID = "certErrorTrust_CaInvalid";
564 break;
565 case SEC_ERROR_UNTRUSTED_ISSUER:
566 errorID = "certErrorTrust_Issuer";
567 break;
568 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
569 errorID = "certErrorTrust_SignatureAlgorithmDisabled";
570 break;
571 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
572 errorID = "certErrorTrust_ExpiredIssuer";
573 break;
574 case SEC_ERROR_UNTRUSTED_CERT:
575 default:
576 errorID = "certErrorTrust_Untrusted";
577 break;
578 }
579 }
581 nsString formattedString;
582 nsresult rv = component->GetPIPNSSBundleString(errorID,
583 formattedString);
584 if (NS_SUCCEEDED(rv))
585 {
586 returnedMessage.Append(formattedString);
587 returnedMessage.Append(NS_LITERAL_STRING("\n"));
588 }
589 }
591 // returns TRUE if SAN was used to produce names
592 // return FALSE if nothing was produced
593 // names => a single name or a list of names
594 // multipleNames => whether multiple names were delivered
595 static bool
596 GetSubjectAltNames(CERTCertificate *nssCert,
597 nsINSSComponent *component,
598 nsString &allNames,
599 uint32_t &nameCount)
600 {
601 allNames.Truncate();
602 nameCount = 0;
604 PLArenaPool *san_arena = nullptr;
605 SECItem altNameExtension = {siBuffer, nullptr, 0 };
606 CERTGeneralName *sanNameList = nullptr;
608 SECStatus rv = CERT_FindCertExtension(nssCert, SEC_OID_X509_SUBJECT_ALT_NAME,
609 &altNameExtension);
610 if (rv != SECSuccess)
611 return false;
613 san_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
614 if (!san_arena)
615 return false;
617 sanNameList = CERT_DecodeAltNameExtension(san_arena, &altNameExtension);
618 if (!sanNameList)
619 return false;
621 SECITEM_FreeItem(&altNameExtension, false);
623 CERTGeneralName *current = sanNameList;
624 do {
625 nsAutoString name;
626 switch (current->type) {
627 case certDNSName:
628 {
629 nsDependentCSubstring nameFromCert(reinterpret_cast<char*>
630 (current->name.other.data),
631 current->name.other.len);
632 // dNSName fields are defined as type IA5String and thus should
633 // be limited to ASCII characters.
634 if (IsASCII(nameFromCert)) {
635 name.Assign(NS_ConvertASCIItoUTF16(nameFromCert));
636 if (!allNames.IsEmpty()) {
637 allNames.Append(NS_LITERAL_STRING(", "));
638 }
639 ++nameCount;
640 allNames.Append(name);
641 }
642 }
643 break;
645 case certIPAddress:
646 {
647 char buf[INET6_ADDRSTRLEN];
648 PRNetAddr addr;
649 if (current->name.other.len == 4) {
650 addr.inet.family = PR_AF_INET;
651 memcpy(&addr.inet.ip, current->name.other.data, current->name.other.len);
652 PR_NetAddrToString(&addr, buf, sizeof(buf));
653 name.AssignASCII(buf);
654 } else if (current->name.other.len == 16) {
655 addr.ipv6.family = PR_AF_INET6;
656 memcpy(&addr.ipv6.ip, current->name.other.data, current->name.other.len);
657 PR_NetAddrToString(&addr, buf, sizeof(buf));
658 name.AssignASCII(buf);
659 } else {
660 /* invalid IP address */
661 }
662 if (!name.IsEmpty()) {
663 if (!allNames.IsEmpty()) {
664 allNames.Append(NS_LITERAL_STRING(", "));
665 }
666 ++nameCount;
667 allNames.Append(name);
668 }
669 break;
670 }
672 default: // all other types of names are ignored
673 break;
674 }
675 current = CERT_GetNextGeneralName(current);
676 } while (current != sanNameList); // double linked
678 PORT_FreeArena(san_arena, false);
679 return true;
680 }
682 static void
683 AppendErrorTextMismatch(const nsString &host,
684 nsIX509Cert* ix509,
685 nsINSSComponent *component,
686 bool wantsHtml,
687 nsString &returnedMessage)
688 {
689 const char16_t *params[1];
690 nsresult rv;
692 mozilla::pkix::ScopedCERTCertificate nssCert;
694 nsCOMPtr<nsIX509Cert2> cert2 = do_QueryInterface(ix509, &rv);
695 if (cert2)
696 nssCert = cert2->GetCert();
698 if (!nssCert) {
699 // We are unable to extract the valid names, say "not valid for name".
700 params[0] = host.get();
701 nsString formattedString;
702 rv = component->PIPBundleFormatStringFromName("certErrorMismatch",
703 params, 1,
704 formattedString);
705 if (NS_SUCCEEDED(rv)) {
706 returnedMessage.Append(formattedString);
707 returnedMessage.Append(NS_LITERAL_STRING("\n"));
708 }
709 return;
710 }
712 nsString allNames;
713 uint32_t nameCount = 0;
714 bool useSAN = false;
716 if (nssCert)
717 useSAN = GetSubjectAltNames(nssCert.get(), component, allNames, nameCount);
719 if (!useSAN) {
720 char *certName = nullptr;
721 // currently CERT_FindNSStringExtension is not being exported by NSS.
722 // If it gets exported, enable the following line.
723 // certName = CERT_FindNSStringExtension(nssCert, SEC_OID_NS_CERT_EXT_SSL_SERVER_NAME);
724 // However, it has been discussed to treat the extension as obsolete and ignore it.
725 if (!certName)
726 certName = CERT_GetCommonName(&nssCert->subject);
727 if (certName) {
728 nsDependentCSubstring commonName(certName, strlen(certName));
729 if (IsUTF8(commonName)) {
730 // Bug 1024781
731 // We should actually check that the common name is a valid dns name or
732 // ip address and not any string value before adding it to the display
733 // list.
734 ++nameCount;
735 allNames.Assign(NS_ConvertUTF8toUTF16(commonName));
736 }
737 PORT_Free(certName);
738 }
739 }
741 if (nameCount > 1) {
742 nsString message;
743 rv = component->GetPIPNSSBundleString("certErrorMismatchMultiple",
744 message);
745 if (NS_SUCCEEDED(rv)) {
746 returnedMessage.Append(message);
747 returnedMessage.Append(NS_LITERAL_STRING("\n "));
748 returnedMessage.Append(allNames);
749 returnedMessage.Append(NS_LITERAL_STRING(" \n"));
750 }
751 }
752 else if (nameCount == 1) {
753 const char16_t *params[1];
754 params[0] = allNames.get();
756 const char *stringID;
757 if (wantsHtml)
758 stringID = "certErrorMismatchSingle2";
759 else
760 stringID = "certErrorMismatchSinglePlain";
762 nsString formattedString;
763 rv = component->PIPBundleFormatStringFromName(stringID,
764 params, 1,
765 formattedString);
766 if (NS_SUCCEEDED(rv)) {
767 returnedMessage.Append(formattedString);
768 returnedMessage.Append(NS_LITERAL_STRING("\n"));
769 }
770 }
771 else { // nameCount == 0
772 nsString message;
773 nsresult rv = component->GetPIPNSSBundleString("certErrorMismatchNoNames",
774 message);
775 if (NS_SUCCEEDED(rv)) {
776 returnedMessage.Append(message);
777 returnedMessage.Append(NS_LITERAL_STRING("\n"));
778 }
779 }
780 }
782 static void
783 GetDateBoundary(nsIX509Cert* ix509,
784 nsString &formattedDate,
785 nsString &nowDate,
786 bool &trueExpired_falseNotYetValid)
787 {
788 trueExpired_falseNotYetValid = true;
789 formattedDate.Truncate();
791 PRTime notAfter, notBefore, timeToUse;
792 nsCOMPtr<nsIX509CertValidity> validity;
793 nsresult rv;
795 rv = ix509->GetValidity(getter_AddRefs(validity));
796 if (NS_FAILED(rv))
797 return;
799 rv = validity->GetNotAfter(¬After);
800 if (NS_FAILED(rv))
801 return;
803 rv = validity->GetNotBefore(¬Before);
804 if (NS_FAILED(rv))
805 return;
807 PRTime now = PR_Now();
808 if (now > notAfter) {
809 timeToUse = notAfter;
810 } else {
811 timeToUse = notBefore;
812 trueExpired_falseNotYetValid = false;
813 }
815 nsCOMPtr<nsIDateTimeFormat> dateTimeFormat(do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv));
816 if (NS_FAILED(rv))
817 return;
819 dateTimeFormat->FormatPRTime(nullptr, kDateFormatShort,
820 kTimeFormatNoSeconds, timeToUse,
821 formattedDate);
822 dateTimeFormat->FormatPRTime(nullptr, kDateFormatShort,
823 kTimeFormatNoSeconds, now,
824 nowDate);
825 }
827 static void
828 AppendErrorTextTime(nsIX509Cert* ix509,
829 nsINSSComponent *component,
830 nsString &returnedMessage)
831 {
832 nsAutoString formattedDate, nowDate;
833 bool trueExpired_falseNotYetValid;
834 GetDateBoundary(ix509, formattedDate, nowDate, trueExpired_falseNotYetValid);
836 const char16_t *params[2];
837 params[0] = formattedDate.get(); // might be empty, if helper function had a problem
838 params[1] = nowDate.get();
840 const char *key = trueExpired_falseNotYetValid ?
841 "certErrorExpiredNow" : "certErrorNotYetValidNow";
842 nsresult rv;
843 nsString formattedString;
844 rv = component->PIPBundleFormatStringFromName(
845 key,
846 params,
847 ArrayLength(params),
848 formattedString);
849 if (NS_SUCCEEDED(rv))
850 {
851 returnedMessage.Append(formattedString);
852 returnedMessage.Append(NS_LITERAL_STRING("\n"));
853 }
854 }
856 static void
857 AppendErrorTextCode(PRErrorCode errorCodeToReport,
858 nsINSSComponent *component,
859 nsString &returnedMessage)
860 {
861 const char *codeName = nsNSSErrors::getDefaultErrorStringName(errorCodeToReport);
862 if (codeName)
863 {
864 nsCString error_id(codeName);
865 ToLowerCase(error_id);
866 NS_ConvertASCIItoUTF16 idU(error_id);
868 const char16_t *params[1];
869 params[0] = idU.get();
871 nsString formattedString;
872 nsresult rv;
873 rv = component->PIPBundleFormatStringFromName("certErrorCodePrefix",
874 params, 1,
875 formattedString);
876 if (NS_SUCCEEDED(rv)) {
877 returnedMessage.Append(NS_LITERAL_STRING("\n"));
878 returnedMessage.Append(formattedString);
879 returnedMessage.Append(NS_LITERAL_STRING("\n"));
880 }
881 else {
882 returnedMessage.Append(NS_LITERAL_STRING(" ("));
883 returnedMessage.Append(idU);
884 returnedMessage.Append(NS_LITERAL_STRING(")"));
885 }
886 }
887 }
889 /* Formats an error message for overridable certificate errors (of type
890 * OverridableCertErrorMessage). Use formatPlainErrorMessage to format
891 * non-overridable cert errors and non-cert-related errors.
892 */
893 static nsresult
894 formatOverridableCertErrorMessage(nsISSLStatus & sslStatus,
895 PRErrorCode errorCodeToReport,
896 const nsXPIDLCString & host, int32_t port,
897 bool suppressPort443,
898 bool wantsHtml,
899 nsString & returnedMessage)
900 {
901 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
903 const char16_t *params[1];
904 nsresult rv;
905 nsAutoString hostWithPort;
906 nsAutoString hostWithoutPort;
908 // For now, hide port when it's 443 and we're reporting the error.
909 // In the future a better mechanism should be used
910 // to make a decision about showing the port number, possibly by requiring
911 // the context object to implement a specific interface.
912 // The motivation is that Mozilla browser would like to hide the port number
913 // in error pages in the common case.
915 hostWithoutPort.AppendASCII(host);
916 if (suppressPort443 && port == 443) {
917 params[0] = hostWithoutPort.get();
918 } else {
919 hostWithPort.AppendASCII(host);
920 hostWithPort.Append(':');
921 hostWithPort.AppendInt(port);
922 params[0] = hostWithPort.get();
923 }
925 nsCOMPtr<nsINSSComponent> component = do_GetService(kNSSComponentCID, &rv);
926 NS_ENSURE_SUCCESS(rv, rv);
928 returnedMessage.Truncate();
929 rv = component->PIPBundleFormatStringFromName("certErrorIntro", params, 1,
930 returnedMessage);
931 NS_ENSURE_SUCCESS(rv, rv);
933 returnedMessage.Append(NS_LITERAL_STRING("\n\n"));
935 RefPtr<nsIX509Cert> ix509;
936 rv = sslStatus.GetServerCert(byRef(ix509));
937 NS_ENSURE_SUCCESS(rv, rv);
939 bool isUntrusted;
940 rv = sslStatus.GetIsUntrusted(&isUntrusted);
941 NS_ENSURE_SUCCESS(rv, rv);
942 if (isUntrusted) {
943 AppendErrorTextUntrusted(errorCodeToReport, hostWithoutPort, ix509,
944 component, returnedMessage);
945 }
947 bool isDomainMismatch;
948 rv = sslStatus.GetIsDomainMismatch(&isDomainMismatch);
949 NS_ENSURE_SUCCESS(rv, rv);
950 if (isDomainMismatch) {
951 AppendErrorTextMismatch(hostWithoutPort, ix509, component, wantsHtml, returnedMessage);
952 }
954 bool isNotValidAtThisTime;
955 rv = sslStatus.GetIsNotValidAtThisTime(&isNotValidAtThisTime);
956 NS_ENSURE_SUCCESS(rv, rv);
957 if (isNotValidAtThisTime) {
958 AppendErrorTextTime(ix509, component, returnedMessage);
959 }
961 AppendErrorTextCode(errorCodeToReport, component, returnedMessage);
963 return NS_OK;
964 }
966 // RememberCertErrorsTable
968 /*static*/ RememberCertErrorsTable*
969 RememberCertErrorsTable::sInstance = nullptr;
971 RememberCertErrorsTable::RememberCertErrorsTable()
972 : mErrorHosts(16)
973 , mMutex("RememberCertErrorsTable::mMutex")
974 {
975 }
977 static nsresult
978 GetHostPortKey(TransportSecurityInfo* infoObject, nsAutoCString &result)
979 {
980 nsresult rv;
982 result.Truncate();
984 nsXPIDLCString hostName;
985 rv = infoObject->GetHostName(getter_Copies(hostName));
986 NS_ENSURE_SUCCESS(rv, rv);
988 int32_t port;
989 rv = infoObject->GetPort(&port);
990 NS_ENSURE_SUCCESS(rv, rv);
992 result.Assign(hostName);
993 result.Append(':');
994 result.AppendInt(port);
996 return NS_OK;
997 }
999 void
1000 RememberCertErrorsTable::RememberCertHasError(TransportSecurityInfo* infoObject,
1001 nsSSLStatus* status,
1002 SECStatus certVerificationResult)
1003 {
1004 nsresult rv;
1006 nsAutoCString hostPortKey;
1007 rv = GetHostPortKey(infoObject, hostPortKey);
1008 if (NS_FAILED(rv))
1009 return;
1011 if (certVerificationResult != SECSuccess) {
1012 NS_ASSERTION(status,
1013 "Must have nsSSLStatus object when remembering flags");
1015 if (!status)
1016 return;
1018 CertStateBits bits;
1019 bits.mIsDomainMismatch = status->mIsDomainMismatch;
1020 bits.mIsNotValidAtThisTime = status->mIsNotValidAtThisTime;
1021 bits.mIsUntrusted = status->mIsUntrusted;
1023 MutexAutoLock lock(mMutex);
1024 mErrorHosts.Put(hostPortKey, bits);
1025 }
1026 else {
1027 MutexAutoLock lock(mMutex);
1028 mErrorHosts.Remove(hostPortKey);
1029 }
1030 }
1032 void
1033 RememberCertErrorsTable::LookupCertErrorBits(TransportSecurityInfo* infoObject,
1034 nsSSLStatus* status)
1035 {
1036 // Get remembered error bits from our cache, because of SSL session caching
1037 // the NSS library potentially hasn't notified us for this socket.
1038 if (status->mHaveCertErrorBits)
1039 // Rather do not modify bits if already set earlier
1040 return;
1042 nsresult rv;
1044 nsAutoCString hostPortKey;
1045 rv = GetHostPortKey(infoObject, hostPortKey);
1046 if (NS_FAILED(rv))
1047 return;
1049 CertStateBits bits;
1050 {
1051 MutexAutoLock lock(mMutex);
1052 if (!mErrorHosts.Get(hostPortKey, &bits))
1053 // No record was found, this host had no cert errors
1054 return;
1055 }
1057 // This host had cert errors, update the bits correctly
1058 status->mHaveCertErrorBits = true;
1059 status->mIsDomainMismatch = bits.mIsDomainMismatch;
1060 status->mIsNotValidAtThisTime = bits.mIsNotValidAtThisTime;
1061 status->mIsUntrusted = bits.mIsUntrusted;
1062 }
1064 void
1065 TransportSecurityInfo::SetStatusErrorBits(nsIX509Cert & cert,
1066 uint32_t collected_errors)
1067 {
1068 MutexAutoLock lock(mMutex);
1070 if (!mSSLStatus)
1071 mSSLStatus = new nsSSLStatus();
1073 mSSLStatus->mServerCert = &cert;
1075 mSSLStatus->mHaveCertErrorBits = true;
1076 mSSLStatus->mIsDomainMismatch =
1077 collected_errors & nsICertOverrideService::ERROR_MISMATCH;
1078 mSSLStatus->mIsNotValidAtThisTime =
1079 collected_errors & nsICertOverrideService::ERROR_TIME;
1080 mSSLStatus->mIsUntrusted =
1081 collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
1083 RememberCertErrorsTable::GetInstance().RememberCertHasError(this,
1084 mSSLStatus,
1085 SECFailure);
1086 }
1088 } } // namespace mozilla::psm