Line data Source code
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/. */
6 :
7 : #include "TransportSecurityInfo.h"
8 :
9 : #include "DateTimeFormat.h"
10 : #include "PSMRunnable.h"
11 : #include "mozilla/Casting.h"
12 : #include "nsComponentManagerUtils.h"
13 : #include "nsIArray.h"
14 : #include "nsICertOverrideService.h"
15 : #include "nsIObjectInputStream.h"
16 : #include "nsIObjectOutputStream.h"
17 : #include "nsIWebProgressListener.h"
18 : #include "nsIX509CertValidity.h"
19 : #include "nsNSSCertHelper.h"
20 : #include "nsNSSCertificate.h"
21 : #include "nsNSSComponent.h"
22 : #include "nsReadableUtils.h"
23 : #include "nsServiceManagerUtils.h"
24 : #include "nsXULAppAPI.h"
25 : #include "pkix/pkixtypes.h"
26 : #include "secerr.h"
27 :
28 : //#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
29 : //reports when doing SSL read/write
30 :
31 : //#define DUMP_BUFFER //Enable this define along with
32 : //DEBUG_SSL_VERBOSE to dump SSL
33 : //read/write buffer to a log.
34 : //Uses PR_LOG except on Mac where
35 : //we always write out to our own
36 : //file.
37 :
38 : namespace mozilla { namespace psm {
39 :
40 0 : TransportSecurityInfo::TransportSecurityInfo()
41 : : mMutex("TransportSecurityInfo::mMutex")
42 : , mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE)
43 : , mSubRequestsBrokenSecurity(0)
44 : , mSubRequestsNoSecurity(0)
45 : , mErrorCode(0)
46 : , mErrorMessageType(SSLErrorMessageType::Plain)
47 0 : , mPort(0)
48 : {
49 0 : }
50 :
51 0 : TransportSecurityInfo::~TransportSecurityInfo()
52 : {
53 0 : nsNSSShutDownPreventionLock locker;
54 0 : if (isAlreadyShutDown())
55 0 : return;
56 :
57 0 : shutdown(ShutdownCalledFrom::Object);
58 0 : }
59 :
60 : void
61 0 : TransportSecurityInfo::virtualDestroyNSSReference()
62 : {
63 0 : }
64 :
65 0 : NS_IMPL_ISUPPORTS(TransportSecurityInfo,
66 : nsITransportSecurityInfo,
67 : nsIInterfaceRequestor,
68 : nsISSLStatusProvider,
69 : nsIAssociatedContentSecurity,
70 : nsISerializable,
71 : nsIClassInfo)
72 :
73 : void
74 0 : TransportSecurityInfo::SetHostName(const char* host)
75 : {
76 0 : mHostName.Assign(host);
77 0 : }
78 :
79 : void
80 0 : TransportSecurityInfo::SetPort(int32_t aPort)
81 : {
82 0 : mPort = aPort;
83 0 : }
84 :
85 : void
86 0 : TransportSecurityInfo::SetOriginAttributes(
87 : const OriginAttributes& aOriginAttributes)
88 : {
89 0 : mOriginAttributes = aOriginAttributes;
90 0 : }
91 :
92 : PRErrorCode
93 0 : TransportSecurityInfo::GetErrorCode() const
94 : {
95 0 : MutexAutoLock lock(mMutex);
96 :
97 0 : return mErrorCode;
98 : }
99 :
100 : void
101 0 : TransportSecurityInfo::SetCanceled(PRErrorCode errorCode,
102 : SSLErrorMessageType errorMessageType)
103 : {
104 0 : MutexAutoLock lock(mMutex);
105 :
106 0 : mErrorCode = errorCode;
107 0 : mErrorMessageType = errorMessageType;
108 0 : mErrorMessageCached.Truncate();
109 0 : }
110 :
111 : NS_IMETHODIMP
112 0 : TransportSecurityInfo::GetSecurityState(uint32_t* state)
113 : {
114 0 : *state = mSecurityState;
115 0 : return NS_OK;
116 : }
117 :
118 : void
119 0 : TransportSecurityInfo::SetSecurityState(uint32_t aState)
120 : {
121 0 : mSecurityState = aState;
122 0 : }
123 :
124 : NS_IMETHODIMP
125 0 : TransportSecurityInfo::GetCountSubRequestsBrokenSecurity(
126 : int32_t *aSubRequestsBrokenSecurity)
127 : {
128 0 : *aSubRequestsBrokenSecurity = mSubRequestsBrokenSecurity;
129 0 : return NS_OK;
130 : }
131 :
132 : NS_IMETHODIMP
133 0 : TransportSecurityInfo::SetCountSubRequestsBrokenSecurity(
134 : int32_t aSubRequestsBrokenSecurity)
135 : {
136 0 : mSubRequestsBrokenSecurity = aSubRequestsBrokenSecurity;
137 0 : return NS_OK;
138 : }
139 :
140 : NS_IMETHODIMP
141 0 : TransportSecurityInfo::GetCountSubRequestsNoSecurity(
142 : int32_t *aSubRequestsNoSecurity)
143 : {
144 0 : *aSubRequestsNoSecurity = mSubRequestsNoSecurity;
145 0 : return NS_OK;
146 : }
147 :
148 : NS_IMETHODIMP
149 0 : TransportSecurityInfo::SetCountSubRequestsNoSecurity(
150 : int32_t aSubRequestsNoSecurity)
151 : {
152 0 : mSubRequestsNoSecurity = aSubRequestsNoSecurity;
153 0 : return NS_OK;
154 : }
155 :
156 : NS_IMETHODIMP
157 0 : TransportSecurityInfo::Flush()
158 : {
159 0 : return NS_OK;
160 : }
161 :
162 : NS_IMETHODIMP
163 0 : TransportSecurityInfo::GetErrorMessage(char16_t** aText)
164 : {
165 0 : NS_ENSURE_ARG_POINTER(aText);
166 0 : *aText = nullptr;
167 :
168 0 : if (!NS_IsMainThread()) {
169 0 : NS_ERROR("nsNSSSocketInfo::GetErrorMessage called off the main thread");
170 0 : return NS_ERROR_NOT_SAME_THREAD;
171 : }
172 :
173 0 : MutexAutoLock lock(mMutex);
174 :
175 0 : if (mErrorMessageCached.IsEmpty()) {
176 0 : nsresult rv = formatErrorMessage(lock,
177 : mErrorCode, mErrorMessageType,
178 0 : true, true, mErrorMessageCached);
179 0 : NS_ENSURE_SUCCESS(rv, rv);
180 : }
181 :
182 0 : *aText = ToNewUnicode(mErrorMessageCached);
183 0 : return *aText ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
184 : }
185 :
186 : void
187 0 : TransportSecurityInfo::GetErrorLogMessage(PRErrorCode errorCode,
188 : SSLErrorMessageType errorMessageType,
189 : nsString &result)
190 : {
191 0 : if (!NS_IsMainThread()) {
192 0 : NS_ERROR("nsNSSSocketInfo::GetErrorLogMessage called off the main thread");
193 0 : return;
194 : }
195 :
196 0 : MutexAutoLock lock(mMutex);
197 : (void) formatErrorMessage(lock, errorCode, errorMessageType,
198 0 : false, false, result);
199 : }
200 :
201 : static nsresult
202 : formatPlainErrorMessage(const nsCString& host, int32_t port,
203 : PRErrorCode err,
204 : bool suppressPort443,
205 : /*out*/ nsString& returnedMessage);
206 :
207 : static nsresult
208 : formatOverridableCertErrorMessage(nsISSLStatus& sslStatus,
209 : PRErrorCode errorCodeToReport,
210 : const nsCString& host, int32_t port,
211 : bool suppressPort443,
212 : bool wantsHtml,
213 : /*out*/ nsString& returnedMessage);
214 :
215 : // XXX: uses nsNSSComponent string bundles off the main thread when called by
216 : // nsNSSSocketInfo::Write().
217 : nsresult
218 0 : TransportSecurityInfo::formatErrorMessage(const MutexAutoLock& /*proofOfLock*/,
219 : PRErrorCode errorCode,
220 : SSLErrorMessageType errorMessageType,
221 : bool wantsHtml, bool suppressPort443,
222 : /*out*/ nsString& result)
223 : {
224 0 : result.Truncate();
225 0 : if (errorCode == 0) {
226 0 : return NS_OK;
227 : }
228 :
229 0 : if (!XRE_IsParentProcess()) {
230 0 : return NS_ERROR_UNEXPECTED;
231 : }
232 :
233 : nsresult rv;
234 0 : MOZ_ASSERT(errorMessageType != SSLErrorMessageType::OverridableCert ||
235 : (mSSLStatus && mSSLStatus->HasServerCert() &&
236 : mSSLStatus->mHaveCertErrorBits),
237 : "formatErrorMessage() called for cert error without cert");
238 0 : if (errorMessageType == SSLErrorMessageType::OverridableCert &&
239 0 : mSSLStatus && mSSLStatus->HasServerCert()) {
240 0 : rv = formatOverridableCertErrorMessage(*mSSLStatus, errorCode,
241 : mHostName, mPort,
242 : suppressPort443,
243 : wantsHtml,
244 0 : result);
245 : } else {
246 0 : rv = formatPlainErrorMessage(mHostName, mPort,
247 : errorCode,
248 : suppressPort443,
249 0 : result);
250 : }
251 :
252 0 : if (NS_FAILED(rv)) {
253 0 : result.Truncate();
254 : }
255 :
256 0 : return rv;
257 : }
258 :
259 : NS_IMETHODIMP
260 0 : TransportSecurityInfo::GetErrorCode(int32_t* state)
261 : {
262 0 : *state = GetErrorCode();
263 0 : return NS_OK;
264 : }
265 :
266 : NS_IMETHODIMP
267 0 : TransportSecurityInfo::GetInterface(const nsIID & uuid, void * *result)
268 : {
269 0 : if (!NS_IsMainThread()) {
270 0 : NS_ERROR("nsNSSSocketInfo::GetInterface called off the main thread");
271 0 : return NS_ERROR_NOT_SAME_THREAD;
272 : }
273 :
274 : nsresult rv;
275 0 : if (!mCallbacks) {
276 0 : nsCOMPtr<nsIInterfaceRequestor> ir = new PipUIContext();
277 0 : rv = ir->GetInterface(uuid, result);
278 : } else {
279 0 : rv = mCallbacks->GetInterface(uuid, result);
280 : }
281 0 : return rv;
282 : }
283 :
284 : // This is a new magic value. However, it re-uses the first 4 bytes
285 : // of the previous value. This is so when older versions attempt to
286 : // read a newer serialized TransportSecurityInfo, they will actually
287 : // fail and return NS_ERROR_FAILURE instead of silently failing.
288 : #define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0x1faa, 0x4169, \
289 : { 0xb0, 0xd2, 0x81, 0x29, 0xec, 0x7c, 0xb1, 0xde } }
290 : static NS_DEFINE_CID(kTransportSecurityInfoMagic, TRANSPORTSECURITYINFOMAGIC);
291 :
292 : NS_IMETHODIMP
293 0 : TransportSecurityInfo::Write(nsIObjectOutputStream* stream)
294 : {
295 0 : nsresult rv = stream->WriteID(kTransportSecurityInfoMagic);
296 0 : if (NS_FAILED(rv)) {
297 0 : return rv;
298 : }
299 :
300 0 : MutexAutoLock lock(mMutex);
301 :
302 0 : rv = stream->Write32(mSecurityState);
303 0 : if (NS_FAILED(rv)) {
304 0 : return rv;
305 : }
306 0 : rv = stream->Write32(mSubRequestsBrokenSecurity);
307 0 : if (NS_FAILED(rv)) {
308 0 : return rv;
309 : }
310 0 : rv = stream->Write32(mSubRequestsNoSecurity);
311 0 : if (NS_FAILED(rv)) {
312 0 : return rv;
313 : }
314 0 : rv = stream->Write32(static_cast<uint32_t>(mErrorCode));
315 0 : if (NS_FAILED(rv)) {
316 0 : return rv;
317 : }
318 0 : if (mErrorMessageCached.IsEmpty()) {
319 : // XXX: uses nsNSSComponent string bundles off the main thread
320 0 : rv = formatErrorMessage(lock, mErrorCode, mErrorMessageType,
321 0 : true, true, mErrorMessageCached);
322 0 : if (NS_FAILED(rv)) {
323 0 : return rv;
324 : }
325 : }
326 0 : rv = stream->WriteWStringZ(mErrorMessageCached.get());
327 0 : if (NS_FAILED(rv)) {
328 0 : return rv;
329 : }
330 :
331 : // For successful connections and for connections with overridable errors,
332 : // mSSLStatus will be non-null. However, for connections with non-overridable
333 : // errors, it will be null.
334 0 : nsCOMPtr<nsISerializable> serializable(mSSLStatus);
335 : rv = NS_WriteOptionalCompoundObject(stream,
336 : serializable,
337 : NS_GET_IID(nsISSLStatus),
338 0 : true);
339 0 : if (NS_FAILED(rv)) {
340 0 : return rv;
341 : }
342 :
343 : rv = NS_WriteOptionalCompoundObject(stream,
344 : mFailedCertChain,
345 : NS_GET_IID(nsIX509CertList),
346 0 : true);
347 0 : if (NS_FAILED(rv)) {
348 0 : return rv;
349 : }
350 :
351 0 : return NS_OK;
352 : }
353 :
354 : NS_IMETHODIMP
355 0 : TransportSecurityInfo::Read(nsIObjectInputStream* stream)
356 : {
357 : nsID id;
358 0 : nsresult rv = stream->ReadID(&id);
359 0 : if (NS_FAILED(rv)) {
360 0 : return rv;
361 : }
362 0 : if (!id.Equals(kTransportSecurityInfoMagic)) {
363 0 : return NS_ERROR_UNEXPECTED;
364 : }
365 :
366 0 : MutexAutoLock lock(mMutex);
367 :
368 0 : rv = stream->Read32(&mSecurityState);
369 0 : if (NS_FAILED(rv)) {
370 0 : return rv;
371 : }
372 : uint32_t subRequestsBrokenSecurity;
373 0 : rv = stream->Read32(&subRequestsBrokenSecurity);
374 0 : if (NS_FAILED(rv)) {
375 0 : return rv;
376 : }
377 0 : if (subRequestsBrokenSecurity >
378 0 : static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
379 0 : return NS_ERROR_UNEXPECTED;
380 : }
381 0 : mSubRequestsBrokenSecurity = subRequestsBrokenSecurity;
382 : uint32_t subRequestsNoSecurity;
383 0 : rv = stream->Read32(&subRequestsNoSecurity);
384 0 : if (NS_FAILED(rv)) {
385 0 : return rv;
386 : }
387 0 : if (subRequestsNoSecurity >
388 0 : static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) {
389 0 : return NS_ERROR_UNEXPECTED;
390 : }
391 0 : mSubRequestsNoSecurity = subRequestsNoSecurity;
392 : uint32_t errorCode;
393 0 : rv = stream->Read32(&errorCode);
394 0 : if (NS_FAILED(rv)) {
395 0 : return rv;
396 : }
397 : // PRErrorCode will be a negative value
398 0 : mErrorCode = static_cast<PRErrorCode>(errorCode);
399 :
400 0 : rv = stream->ReadString(mErrorMessageCached);
401 0 : if (NS_FAILED(rv)) {
402 0 : return rv;
403 : }
404 :
405 : // For successful connections and for connections with overridable errors,
406 : // mSSLStatus will be non-null. For connections with non-overridable errors,
407 : // it will be null.
408 0 : nsCOMPtr<nsISupports> supports;
409 0 : rv = NS_ReadOptionalObject(stream, true, getter_AddRefs(supports));
410 0 : if (NS_FAILED(rv)) {
411 0 : return rv;
412 : }
413 0 : mSSLStatus = BitwiseCast<nsSSLStatus*, nsISupports*>(supports.get());
414 :
415 0 : nsCOMPtr<nsISupports> failedCertChainSupports;
416 0 : rv = NS_ReadOptionalObject(stream, true, getter_AddRefs(failedCertChainSupports));
417 0 : if (NS_FAILED(rv)) {
418 0 : return rv;
419 : }
420 0 : mFailedCertChain = do_QueryInterface(failedCertChainSupports);
421 :
422 0 : return NS_OK;
423 : }
424 :
425 : NS_IMETHODIMP
426 0 : TransportSecurityInfo::GetInterfaces(uint32_t *count, nsIID * **array)
427 : {
428 0 : *count = 0;
429 0 : *array = nullptr;
430 0 : return NS_OK;
431 : }
432 :
433 : NS_IMETHODIMP
434 0 : TransportSecurityInfo::GetScriptableHelper(nsIXPCScriptable **_retval)
435 : {
436 0 : *_retval = nullptr;
437 0 : return NS_OK;
438 : }
439 :
440 : NS_IMETHODIMP
441 0 : TransportSecurityInfo::GetContractID(char * *aContractID)
442 : {
443 0 : *aContractID = nullptr;
444 0 : return NS_OK;
445 : }
446 :
447 : NS_IMETHODIMP
448 0 : TransportSecurityInfo::GetClassDescription(char * *aClassDescription)
449 : {
450 0 : *aClassDescription = nullptr;
451 0 : return NS_OK;
452 : }
453 :
454 : NS_IMETHODIMP
455 0 : TransportSecurityInfo::GetClassID(nsCID * *aClassID)
456 : {
457 0 : *aClassID = (nsCID*) moz_xmalloc(sizeof(nsCID));
458 0 : if (!*aClassID)
459 0 : return NS_ERROR_OUT_OF_MEMORY;
460 0 : return GetClassIDNoAlloc(*aClassID);
461 : }
462 :
463 : NS_IMETHODIMP
464 0 : TransportSecurityInfo::GetFlags(uint32_t *aFlags)
465 : {
466 0 : *aFlags = 0;
467 0 : return NS_OK;
468 : }
469 :
470 : static NS_DEFINE_CID(kNSSSocketInfoCID, TRANSPORTSECURITYINFO_CID);
471 :
472 : NS_IMETHODIMP
473 0 : TransportSecurityInfo::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
474 : {
475 0 : *aClassIDNoAlloc = kNSSSocketInfoCID;
476 0 : return NS_OK;
477 : }
478 :
479 : nsresult
480 0 : TransportSecurityInfo::GetSSLStatus(nsISSLStatus** _result)
481 : {
482 0 : NS_ENSURE_ARG_POINTER(_result);
483 :
484 0 : *_result = mSSLStatus;
485 0 : NS_IF_ADDREF(*_result);
486 :
487 0 : return NS_OK;
488 : }
489 :
490 : void
491 0 : TransportSecurityInfo::SetSSLStatus(nsSSLStatus *aSSLStatus)
492 : {
493 0 : mSSLStatus = aSSLStatus;
494 0 : }
495 :
496 : /* Formats an error message for non-certificate-related SSL errors
497 : * and non-overridable certificate errors (both are of type
498 : * PlainErrormMessage). Use formatOverridableCertErrorMessage
499 : * for overridable cert errors.
500 : */
501 : static nsresult
502 0 : formatPlainErrorMessage(const nsCString& host, int32_t port,
503 : PRErrorCode err,
504 : bool suppressPort443,
505 : /*out*/ nsString& returnedMessage)
506 : {
507 : static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
508 :
509 : const char16_t *params[1];
510 : nsresult rv;
511 :
512 0 : nsCOMPtr<nsINSSComponent> component = do_GetService(kNSSComponentCID, &rv);
513 0 : NS_ENSURE_SUCCESS(rv, rv);
514 :
515 0 : if (host.Length())
516 : {
517 : // For now, hide port when it's 443 and we're reporting the error.
518 : // In the future a better mechanism should be used
519 : // to make a decision about showing the port number, possibly by requiring
520 : // the context object to implement a specific interface.
521 : // The motivation is that Mozilla browser would like to hide the port number
522 : // in error pages in the common case.
523 :
524 0 : NS_ConvertASCIItoUTF16 hostWithPort(host);
525 0 : if (!suppressPort443 || port != 443) {
526 0 : hostWithPort.Append(':');
527 0 : hostWithPort.AppendInt(port);
528 : }
529 0 : params[0] = hostWithPort.get();
530 :
531 0 : nsString formattedString;
532 0 : rv = component->PIPBundleFormatStringFromName("SSLConnectionErrorPrefix",
533 0 : params, 1, formattedString);
534 0 : if (NS_SUCCEEDED(rv))
535 : {
536 0 : returnedMessage.Append(formattedString);
537 0 : returnedMessage.AppendLiteral("\n\n");
538 : }
539 : }
540 :
541 0 : nsString explanation;
542 0 : rv = nsNSSErrors::getErrorMessageFromCode(err, component, explanation);
543 0 : if (NS_SUCCEEDED(rv))
544 0 : returnedMessage.Append(explanation);
545 :
546 0 : return NS_OK;
547 : }
548 :
549 : static void
550 0 : AppendErrorTextUntrusted(PRErrorCode errTrust,
551 : const nsString &host,
552 : nsIX509Cert* ix509,
553 : nsINSSComponent *component,
554 : nsString &returnedMessage)
555 : {
556 0 : const char* errorID = nullptr;
557 0 : const char* errorID2 = nullptr;
558 0 : const char* errorID3 = nullptr;
559 : bool isSelfSigned;
560 0 : if (NS_SUCCEEDED(ix509->GetIsSelfSigned(&isSelfSigned)) && isSelfSigned) {
561 0 : errorID = "certErrorTrust_SelfSigned";
562 : }
563 :
564 0 : if (!errorID) {
565 0 : switch (errTrust) {
566 : case SEC_ERROR_UNKNOWN_ISSUER:
567 0 : errorID = "certErrorTrust_UnknownIssuer";
568 0 : errorID2 = "certErrorTrust_UnknownIssuer2";
569 0 : errorID3 = "certErrorTrust_UnknownIssuer3";
570 0 : break;
571 : case SEC_ERROR_CA_CERT_INVALID:
572 0 : errorID = "certErrorTrust_CaInvalid";
573 0 : break;
574 : case SEC_ERROR_UNTRUSTED_ISSUER:
575 0 : errorID = "certErrorTrust_Issuer";
576 0 : break;
577 : case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
578 0 : errorID = "certErrorTrust_SignatureAlgorithmDisabled";
579 0 : break;
580 : case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
581 0 : errorID = "certErrorTrust_ExpiredIssuer";
582 0 : break;
583 : case SEC_ERROR_UNTRUSTED_CERT:
584 : default:
585 0 : errorID = "certErrorTrust_Untrusted";
586 0 : break;
587 : }
588 : }
589 :
590 0 : const char* errorIDs[] = { errorID, errorID2, errorID3 };
591 0 : for (size_t i = 0; i < ArrayLength(errorIDs); i++) {
592 0 : if (!errorIDs[i]) {
593 0 : break;
594 : }
595 :
596 0 : nsString formattedString;
597 0 : nsresult rv = component->GetPIPNSSBundleString(errorIDs[i], formattedString);
598 0 : if (NS_SUCCEEDED(rv)) {
599 0 : returnedMessage.Append(formattedString);
600 0 : returnedMessage.Append('\n');
601 : }
602 : }
603 0 : }
604 :
605 : // Returns the number of dNSName or iPAddress entries encountered in the
606 : // subject alternative name extension of the certificate.
607 : // Returns zero if the extension is not present, could not be decoded, or if it
608 : // does not contain any dNSName or iPAddress entries.
609 : static uint32_t
610 0 : GetSubjectAltNames(CERTCertificate* nssCert, nsString& allNames)
611 : {
612 0 : allNames.Truncate();
613 :
614 0 : ScopedAutoSECItem altNameExtension;
615 : SECStatus rv = CERT_FindCertExtension(nssCert, SEC_OID_X509_SUBJECT_ALT_NAME,
616 0 : &altNameExtension);
617 0 : if (rv != SECSuccess) {
618 0 : return 0;
619 : }
620 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
621 0 : if (!arena) {
622 0 : return 0;
623 : }
624 0 : CERTGeneralName* sanNameList(CERT_DecodeAltNameExtension(arena.get(),
625 0 : &altNameExtension));
626 0 : if (!sanNameList) {
627 0 : return 0;
628 : }
629 :
630 0 : uint32_t nameCount = 0;
631 0 : CERTGeneralName* current = sanNameList;
632 0 : do {
633 0 : nsAutoString name;
634 0 : switch (current->type) {
635 : case certDNSName:
636 : {
637 0 : nsDependentCSubstring nameFromCert(BitwiseCast<char*, unsigned char*>(
638 : current->name.other.data),
639 0 : current->name.other.len);
640 : // dNSName fields are defined as type IA5String and thus should
641 : // be limited to ASCII characters.
642 0 : if (IsASCII(nameFromCert)) {
643 0 : name.Assign(NS_ConvertASCIItoUTF16(nameFromCert));
644 0 : if (!allNames.IsEmpty()) {
645 0 : allNames.AppendLiteral(", ");
646 : }
647 0 : ++nameCount;
648 0 : allNames.Append(name);
649 : }
650 : }
651 0 : break;
652 :
653 : case certIPAddress:
654 : {
655 : char buf[INET6_ADDRSTRLEN];
656 : PRNetAddr addr;
657 0 : if (current->name.other.len == 4) {
658 0 : addr.inet.family = PR_AF_INET;
659 0 : memcpy(&addr.inet.ip, current->name.other.data, current->name.other.len);
660 0 : PR_NetAddrToString(&addr, buf, sizeof(buf));
661 0 : name.AssignASCII(buf);
662 0 : } else if (current->name.other.len == 16) {
663 0 : addr.ipv6.family = PR_AF_INET6;
664 0 : memcpy(&addr.ipv6.ip, current->name.other.data, current->name.other.len);
665 0 : PR_NetAddrToString(&addr, buf, sizeof(buf));
666 0 : name.AssignASCII(buf);
667 : } else {
668 : /* invalid IP address */
669 : }
670 0 : if (!name.IsEmpty()) {
671 0 : if (!allNames.IsEmpty()) {
672 0 : allNames.AppendLiteral(", ");
673 : }
674 0 : ++nameCount;
675 0 : allNames.Append(name);
676 : }
677 0 : break;
678 : }
679 :
680 : default: // all other types of names are ignored
681 0 : break;
682 : }
683 0 : current = CERT_GetNextGeneralName(current);
684 0 : } while (current != sanNameList); // double linked
685 :
686 0 : return nameCount;
687 : }
688 :
689 : static nsresult
690 0 : AppendErrorTextMismatch(const nsString& host, nsIX509Cert* ix509,
691 : nsINSSComponent* component, bool wantsHtml,
692 : nsString& returnedMessage)
693 : {
694 : // Prepare a default "not valid for <hostname>" string in case anything
695 : // goes wrong (or in case the certificate is not valid for any hostnames).
696 0 : nsAutoString notValidForHostnameString;
697 : const char16_t* params[1];
698 0 : params[0] = host.get();
699 : nsresult rv = component->PIPBundleFormatStringFromName(
700 0 : "certErrorMismatch", params, 1, notValidForHostnameString);
701 0 : if (NS_FAILED(rv)) {
702 0 : return rv;
703 : }
704 0 : notValidForHostnameString.Append('\n');
705 :
706 0 : UniqueCERTCertificate nssCert(ix509->GetCert());
707 0 : if (!nssCert) {
708 0 : returnedMessage.Append(notValidForHostnameString);
709 0 : return NS_OK;
710 : }
711 :
712 0 : nsAutoString allNames;
713 0 : uint32_t nameCount = GetSubjectAltNames(nssCert.get(), allNames);
714 0 : if (nameCount == 0) {
715 0 : returnedMessage.Append(notValidForHostnameString);
716 0 : } else if (nameCount > 1) {
717 0 : nsString message;
718 0 : rv = component->GetPIPNSSBundleString("certErrorMismatchMultiple", message);
719 0 : if (NS_FAILED(rv)) {
720 0 : return rv;
721 : }
722 0 : returnedMessage.Append(message);
723 0 : returnedMessage.AppendLiteral("\n ");
724 0 : returnedMessage.Append(allNames);
725 0 : returnedMessage.AppendLiteral(" \n");
726 0 : } else if (nameCount == 1) {
727 0 : params[0] = allNames.get();
728 :
729 0 : const char* stringID = wantsHtml ? "certErrorMismatchSingle2"
730 0 : : "certErrorMismatchSinglePlain";
731 0 : nsAutoString formattedString;
732 : rv = component->PIPBundleFormatStringFromName(stringID, params, 1,
733 0 : formattedString);
734 0 : if (NS_FAILED(rv)) {
735 0 : return rv;
736 : }
737 0 : returnedMessage.Append(formattedString);
738 0 : returnedMessage.Append('\n');
739 : }
740 :
741 0 : return NS_OK;
742 : }
743 :
744 : static void
745 0 : GetDateBoundary(nsIX509Cert* ix509,
746 : nsString &formattedDate,
747 : nsString &nowDate,
748 : bool &trueExpired_falseNotYetValid)
749 : {
750 0 : trueExpired_falseNotYetValid = true;
751 0 : formattedDate.Truncate();
752 :
753 : PRTime notAfter, notBefore, timeToUse;
754 0 : nsCOMPtr<nsIX509CertValidity> validity;
755 : nsresult rv;
756 :
757 0 : rv = ix509->GetValidity(getter_AddRefs(validity));
758 0 : if (NS_FAILED(rv))
759 0 : return;
760 :
761 0 : rv = validity->GetNotAfter(¬After);
762 0 : if (NS_FAILED(rv))
763 0 : return;
764 :
765 0 : rv = validity->GetNotBefore(¬Before);
766 0 : if (NS_FAILED(rv))
767 0 : return;
768 :
769 0 : PRTime now = PR_Now();
770 0 : if (now > notAfter) {
771 0 : timeToUse = notAfter;
772 : } else {
773 0 : timeToUse = notBefore;
774 0 : trueExpired_falseNotYetValid = false;
775 : }
776 :
777 0 : DateTimeFormat::FormatPRTime(kDateFormatLong, kTimeFormatNoSeconds,
778 0 : timeToUse, formattedDate);
779 0 : DateTimeFormat::FormatPRTime(kDateFormatLong, kTimeFormatNoSeconds,
780 0 : now, nowDate);
781 : }
782 :
783 : static void
784 0 : AppendErrorTextTime(nsIX509Cert* ix509,
785 : nsINSSComponent *component,
786 : nsString &returnedMessage)
787 : {
788 0 : nsAutoString formattedDate, nowDate;
789 : bool trueExpired_falseNotYetValid;
790 0 : GetDateBoundary(ix509, formattedDate, nowDate, trueExpired_falseNotYetValid);
791 :
792 : const char16_t *params[2];
793 0 : params[0] = formattedDate.get(); // might be empty, if helper function had a problem
794 0 : params[1] = nowDate.get();
795 :
796 0 : const char *key = trueExpired_falseNotYetValid ?
797 0 : "certErrorExpiredNow" : "certErrorNotYetValidNow";
798 : nsresult rv;
799 0 : nsString formattedString;
800 0 : rv = component->PIPBundleFormatStringFromName(
801 : key,
802 : params,
803 0 : ArrayLength(params),
804 0 : formattedString);
805 0 : if (NS_SUCCEEDED(rv))
806 : {
807 0 : returnedMessage.Append(formattedString);
808 0 : returnedMessage.Append('\n');
809 : }
810 0 : }
811 :
812 : static void
813 0 : AppendErrorTextCode(PRErrorCode errorCodeToReport,
814 : nsINSSComponent *component,
815 : nsString &returnedMessage)
816 : {
817 0 : const char *codeName = nsNSSErrors::getDefaultErrorStringName(errorCodeToReport);
818 0 : if (codeName)
819 : {
820 0 : nsCString error_id(codeName);
821 0 : NS_ConvertASCIItoUTF16 idU(error_id);
822 :
823 : const char16_t *params[1];
824 0 : params[0] = idU.get();
825 :
826 0 : nsString formattedString;
827 : nsresult rv;
828 : rv = component->PIPBundleFormatStringFromName("certErrorCodePrefix2",
829 : params, 1,
830 0 : formattedString);
831 0 : if (NS_SUCCEEDED(rv)) {
832 0 : returnedMessage.Append('\n');
833 0 : returnedMessage.Append(formattedString);
834 0 : returnedMessage.Append('\n');
835 : }
836 : else {
837 0 : returnedMessage.AppendLiteral(" (");
838 0 : returnedMessage.Append(idU);
839 0 : returnedMessage.Append(')');
840 : }
841 : }
842 0 : }
843 :
844 : /* Formats an error message for overridable certificate errors (of type
845 : * OverridableCertErrorMessage). Use formatPlainErrorMessage to format
846 : * non-overridable cert errors and non-cert-related errors.
847 : */
848 : static nsresult
849 0 : formatOverridableCertErrorMessage(nsISSLStatus& sslStatus,
850 : PRErrorCode errorCodeToReport,
851 : const nsCString& host, int32_t port,
852 : bool suppressPort443,
853 : bool wantsHtml,
854 : /*out*/ nsString& returnedMessage)
855 : {
856 : static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
857 :
858 : const char16_t *params[1];
859 : nsresult rv;
860 0 : NS_ConvertASCIItoUTF16 hostWithPort(host);
861 0 : NS_ConvertASCIItoUTF16 hostWithoutPort(host);
862 :
863 : // For now, hide port when it's 443 and we're reporting the error.
864 : // In the future a better mechanism should be used
865 : // to make a decision about showing the port number, possibly by requiring
866 : // the context object to implement a specific interface.
867 : // The motivation is that Mozilla browser would like to hide the port number
868 : // in error pages in the common case.
869 0 : if (suppressPort443 && port == 443) {
870 0 : params[0] = hostWithoutPort.get();
871 : } else {
872 0 : hostWithPort.Append(':');
873 0 : hostWithPort.AppendInt(port);
874 0 : params[0] = hostWithPort.get();
875 : }
876 :
877 0 : nsCOMPtr<nsINSSComponent> component = do_GetService(kNSSComponentCID, &rv);
878 0 : NS_ENSURE_SUCCESS(rv, rv);
879 :
880 0 : returnedMessage.Truncate();
881 0 : rv = component->PIPBundleFormatStringFromName("certErrorIntro", params, 1,
882 0 : returnedMessage);
883 0 : NS_ENSURE_SUCCESS(rv, rv);
884 :
885 0 : returnedMessage.AppendLiteral("\n\n");
886 :
887 0 : RefPtr<nsIX509Cert> ix509;
888 0 : rv = sslStatus.GetServerCert(getter_AddRefs(ix509));
889 0 : NS_ENSURE_SUCCESS(rv, rv);
890 :
891 : bool isUntrusted;
892 0 : rv = sslStatus.GetIsUntrusted(&isUntrusted);
893 0 : NS_ENSURE_SUCCESS(rv, rv);
894 0 : if (isUntrusted) {
895 0 : AppendErrorTextUntrusted(errorCodeToReport, hostWithoutPort, ix509,
896 0 : component, returnedMessage);
897 : }
898 :
899 : bool isDomainMismatch;
900 0 : rv = sslStatus.GetIsDomainMismatch(&isDomainMismatch);
901 0 : NS_ENSURE_SUCCESS(rv, rv);
902 0 : if (isDomainMismatch) {
903 0 : rv = AppendErrorTextMismatch(hostWithoutPort, ix509, component, wantsHtml,
904 : returnedMessage);
905 0 : NS_ENSURE_SUCCESS(rv, rv);
906 : }
907 :
908 : bool isNotValidAtThisTime;
909 0 : rv = sslStatus.GetIsNotValidAtThisTime(&isNotValidAtThisTime);
910 0 : NS_ENSURE_SUCCESS(rv, rv);
911 0 : if (isNotValidAtThisTime) {
912 0 : AppendErrorTextTime(ix509, component, returnedMessage);
913 : }
914 :
915 0 : AppendErrorTextCode(errorCodeToReport, component, returnedMessage);
916 :
917 0 : return NS_OK;
918 : }
919 :
920 : // RememberCertErrorsTable
921 :
922 : /*static*/ RememberCertErrorsTable*
923 : RememberCertErrorsTable::sInstance = nullptr;
924 :
925 1 : RememberCertErrorsTable::RememberCertErrorsTable()
926 : : mErrorHosts()
927 1 : , mMutex("RememberCertErrorsTable::mMutex")
928 : {
929 1 : }
930 :
931 : static nsresult
932 0 : GetHostPortKey(TransportSecurityInfo* infoObject, /*out*/ nsCString& result)
933 : {
934 0 : MOZ_ASSERT(infoObject);
935 0 : NS_ENSURE_ARG(infoObject);
936 :
937 0 : result.Truncate();
938 :
939 0 : result.Assign(infoObject->GetHostName());
940 0 : result.Append(':');
941 0 : result.AppendInt(infoObject->GetPort());
942 :
943 0 : return NS_OK;
944 : }
945 :
946 : void
947 0 : RememberCertErrorsTable::RememberCertHasError(TransportSecurityInfo* infoObject,
948 : nsSSLStatus* status,
949 : SECStatus certVerificationResult)
950 : {
951 : nsresult rv;
952 :
953 0 : nsAutoCString hostPortKey;
954 0 : rv = GetHostPortKey(infoObject, hostPortKey);
955 0 : if (NS_FAILED(rv))
956 0 : return;
957 :
958 0 : if (certVerificationResult != SECSuccess) {
959 0 : MOZ_ASSERT(status, "Must have nsSSLStatus object when remembering flags");
960 :
961 0 : if (!status)
962 0 : return;
963 :
964 : CertStateBits bits;
965 0 : bits.mIsDomainMismatch = status->mIsDomainMismatch;
966 0 : bits.mIsNotValidAtThisTime = status->mIsNotValidAtThisTime;
967 0 : bits.mIsUntrusted = status->mIsUntrusted;
968 :
969 0 : MutexAutoLock lock(mMutex);
970 0 : mErrorHosts.Put(hostPortKey, bits);
971 : }
972 : else {
973 0 : MutexAutoLock lock(mMutex);
974 0 : mErrorHosts.Remove(hostPortKey);
975 : }
976 : }
977 :
978 : void
979 0 : RememberCertErrorsTable::LookupCertErrorBits(TransportSecurityInfo* infoObject,
980 : nsSSLStatus* status)
981 : {
982 : // Get remembered error bits from our cache, because of SSL session caching
983 : // the NSS library potentially hasn't notified us for this socket.
984 0 : if (status->mHaveCertErrorBits)
985 : // Rather do not modify bits if already set earlier
986 0 : return;
987 :
988 : nsresult rv;
989 :
990 0 : nsAutoCString hostPortKey;
991 0 : rv = GetHostPortKey(infoObject, hostPortKey);
992 0 : if (NS_FAILED(rv))
993 0 : return;
994 :
995 : CertStateBits bits;
996 : {
997 0 : MutexAutoLock lock(mMutex);
998 0 : if (!mErrorHosts.Get(hostPortKey, &bits))
999 : // No record was found, this host had no cert errors
1000 0 : return;
1001 : }
1002 :
1003 : // This host had cert errors, update the bits correctly
1004 0 : status->mHaveCertErrorBits = true;
1005 0 : status->mIsDomainMismatch = bits.mIsDomainMismatch;
1006 0 : status->mIsNotValidAtThisTime = bits.mIsNotValidAtThisTime;
1007 0 : status->mIsUntrusted = bits.mIsUntrusted;
1008 : }
1009 :
1010 : void
1011 0 : TransportSecurityInfo::SetStatusErrorBits(nsNSSCertificate* cert,
1012 : uint32_t collected_errors)
1013 : {
1014 0 : MutexAutoLock lock(mMutex);
1015 :
1016 0 : if (!mSSLStatus) {
1017 0 : mSSLStatus = new nsSSLStatus();
1018 : }
1019 :
1020 0 : mSSLStatus->SetServerCert(cert, EVStatus::NotEV);
1021 :
1022 0 : mSSLStatus->mHaveCertErrorBits = true;
1023 0 : mSSLStatus->mIsDomainMismatch =
1024 0 : collected_errors & nsICertOverrideService::ERROR_MISMATCH;
1025 0 : mSSLStatus->mIsNotValidAtThisTime =
1026 0 : collected_errors & nsICertOverrideService::ERROR_TIME;
1027 0 : mSSLStatus->mIsUntrusted =
1028 0 : collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
1029 :
1030 0 : RememberCertErrorsTable::GetInstance().RememberCertHasError(this,
1031 : mSSLStatus,
1032 0 : SECFailure);
1033 0 : }
1034 :
1035 : NS_IMETHODIMP
1036 0 : TransportSecurityInfo::GetFailedCertChain(nsIX509CertList** _result)
1037 : {
1038 0 : MOZ_ASSERT(_result);
1039 :
1040 0 : *_result = mFailedCertChain;
1041 0 : NS_IF_ADDREF(*_result);
1042 :
1043 0 : return NS_OK;
1044 : }
1045 :
1046 : nsresult
1047 0 : TransportSecurityInfo::SetFailedCertChain(UniqueCERTCertList certList)
1048 : {
1049 0 : nsNSSShutDownPreventionLock lock;
1050 0 : if (isAlreadyShutDown()) {
1051 0 : return NS_ERROR_NOT_AVAILABLE;
1052 : }
1053 :
1054 : // nsNSSCertList takes ownership of certList
1055 0 : mFailedCertChain = new nsNSSCertList(Move(certList), lock);
1056 :
1057 0 : return NS_OK;
1058 : }
1059 :
1060 9 : } } // namespace mozilla::psm
|