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 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Brian Ryner <bryner@brianryner.com>
25 : * Terry Hayes <thayes@netscape.com>
26 : * Kai Engert <kengert@redhat.com>
27 : * Petr Kostka <petr.kostka@st.com>
28 : * Honza Bambas <honzab@firemni.cz>
29 : *
30 : * Alternatively, the contents of this file may be used under the terms of
31 : * either the GNU General Public License Version 2 or later (the "GPL"), or
32 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 : * in which case the provisions of the GPL or the LGPL are applicable instead
34 : * of those above. If you wish to allow use of your version of this file only
35 : * under the terms of either the GPL or the LGPL, and not to allow others to
36 : * use your version of this file under the terms of the MPL, indicate your
37 : * decision by deleting the provisions above and replace them with the notice
38 : * and other provisions required by the GPL or the LGPL. If you do not delete
39 : * the provisions above, a recipient may use your version of this file under
40 : * the terms of any one of the MPL, the GPL or the LGPL.
41 : *
42 : * ***** END LICENSE BLOCK ***** */
43 :
44 : /*
45 : * For connections that are not processed on the socket transport thread, we do
46 : * NOT use the async logic described below. Instead, we authenticate the
47 : * certificate on the thread that the connection's I/O happens on,
48 : * synchronously. This allows us to do certificate verification for blocking
49 : * (not non-blocking) sockets and sockets that have their I/O processed on a
50 : * thread other than the socket transport service thread. Also, we DO NOT
51 : * support blocking sockets on the socket transport service thread at all.
52 : *
53 : * During certificate authentication, we call CERT_PKIXVerifyCert or
54 : * CERT_VerifyCert. These functions may make zero or more HTTP requests
55 : * for OCSP responses, CRLs, intermediate certificates, etc. Our fetching logic
56 : * for these requests processes them on the socket transport service thread.
57 : *
58 : * If the connection for which we are verifying the certificate is happening
59 : * on the socket transport thread (the usually case, at least for HTTP), then
60 : * if our cert auth hook were to call the CERT_*Verify* functions directly,
61 : * there would be a deadlock: The CERT_*Verify* function would cause an event
62 : * to be asynchronously posted to the socket transport thread, and then it
63 : * would block the socket transport thread waiting to be notified of the HTTP
64 : * response. However, the HTTP request would never actually be processed
65 : * because the socket transport thread would be blocked and so it wouldn't be
66 : * able process HTTP requests. (i.e. Deadlock.)
67 : *
68 : * Consequently, when we are asked to verify a certificate on the socket
69 : * transport service thread, we must always call the CERT_*Verify* cert
70 : * functions on another thread. To accomplish this, our auth cert hook
71 : * dispatches a SSLServerCertVerificationJob to a pool of background threads,
72 : * and then immediatley return SECWouldBlock to libssl. These jobs are where
73 : * the CERT_*Verify* functions are actually called.
74 : *
75 : * When our auth cert hook returns SECWouldBlock, libssl will carry on the
76 : * handshake while we validate the certificate. This will free up the socket
77 : * transport thread so that HTTP requests--in particular, the OCSP/CRL/cert
78 : * requests needed for cert verification as mentioned above--can be processed.
79 : *
80 : * Once the CERT_*Verify* function returns, the cert verification job
81 : * dispatches a SSLServerCertVerificationResult to the socket transport thread;
82 : * the SSLServerCertVerificationResult will notify libssl that the certificate
83 : * authentication is complete. Once libssl is notified that the authentication
84 : * is complete, it will continue the SSL handshake (if it hasn't already
85 : * finished) and it will begin allowing us to send/receive data on the
86 : * connection.
87 : *
88 : * Timeline of events (for connections managed by the socket transport service):
89 : *
90 : * * libssl calls SSLServerCertVerificationJob::Dispatch on the socket
91 : * transport thread.
92 : * * SSLServerCertVerificationJob::Dispatch queues a job
93 : * (instance of SSLServerCertVerificationJob) to its background thread
94 : * pool and returns.
95 : * * One of the background threads calls CERT_*Verify*, which may enqueue
96 : * some HTTP request(s) onto the socket transport thread, and then
97 : * blocks that background thread waiting for the responses and/or timeouts
98 : * or errors for those requests.
99 : * * Once those HTTP responses have all come back or failed, the
100 : * CERT_*Verify* function returns a result indicating that the validation
101 : * succeeded or failed.
102 : * * If the validation succeeded, then a SSLServerCertVerificationResult
103 : * event is posted to the socket transport thread, and the cert
104 : * verification thread becomes free to verify other certificates.
105 : * * Otherwise, a CertErrorRunnable is posted to the socket transport thread
106 : * and then to the main thread (blocking both, see CertErrorRunnable) to
107 : * do cert override processing and bad cert listener notification. Then
108 : * the cert verification thread becomes free to verify other certificates.
109 : * * After processing cert overrides, the CertErrorRunnable will dispatch a
110 : * SSLServerCertVerificationResult event to the socket transport thread to
111 : * notify it of the result of the override processing; then it returns,
112 : * freeing up the main thread.
113 : * * The SSLServerCertVerificationResult event will either wake up the
114 : * socket (using SSL_RestartHandshakeAfterServerCert) if validation
115 : * succeeded or there was an error override, or it will set an error flag
116 : * so that the next I/O operation on the socket will fail, causing the
117 : * socket transport thread to close the connection.
118 : *
119 : * Cert override processing must happen on the main thread because it accesses
120 : * the nsICertOverrideService, and that service must be accessed on the main
121 : * thread because some extensions (Selenium, in particular) replace it with a
122 : * Javascript implementation, and chrome JS must always be run on the main
123 : * thread.
124 : *
125 : * SSLServerCertVerificationResult must be dispatched to the socket transport
126 : * thread because we must only call SSL_* functions on the socket transport
127 : * thread since they may do I/O, because many parts of nsNSSSocketInfo and
128 : * the PSM NSS I/O layer are not thread-safe, and because we need the event to
129 : * interrupt the PR_Poll that may waiting for I/O on the socket for which we
130 : * are validating the cert.
131 : */
132 :
133 : #include "SSLServerCertVerification.h"
134 : #include "nsIBadCertListener2.h"
135 : #include "nsICertOverrideService.h"
136 : #include "nsIStrictTransportSecurityService.h"
137 : #include "nsNSSComponent.h"
138 : #include "nsNSSCleaner.h"
139 : #include "nsRecentBadCerts.h"
140 : #include "nsNSSIOLayer.h"
141 :
142 : #include "mozilla/Assertions.h"
143 : #include "nsIThreadPool.h"
144 : #include "nsXPCOMCIDInternal.h"
145 : #include "nsComponentManagerUtils.h"
146 : #include "nsServiceManagerUtils.h"
147 : #include "PSMRunnable.h"
148 :
149 : #include "ssl.h"
150 : #include "secerr.h"
151 : #include "secport.h"
152 : #include "sslerr.h"
153 :
154 : #ifdef PR_LOGGING
155 : extern PRLogModuleInfo* gPIPNSSLog;
156 : #endif
157 :
158 : namespace mozilla { namespace psm {
159 :
160 : namespace {
161 :
162 : NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
163 :
164 8 : NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
165 0 : NSSCleanupAutoPtrClass_WithParam(PRArenaPool, PORT_FreeArena, FalseParam, false)
166 :
167 : // do not use a nsCOMPtr to avoid static initializer/destructor
168 : nsIThreadPool * gCertVerificationThreadPool = nsnull;
169 : } // unnamed namespace
170 :
171 : // Called when the socket transport thread starts, to initialize the SSL cert
172 : // verification thread pool. By tying the thread pool startup/shutdown directly
173 : // to the STS thread's lifetime, we ensure that they are *always* available for
174 : // SSL connections and that there are no races during startup and especially
175 : // shutdown. (Previously, we have had multiple problems with races in PSM
176 : // background threads, and the race-prevention/shutdown logic used there is
177 : // brittle. Since this service is critical to things like downloading updates,
178 : // we take no chances.) Also, by doing things this way, we avoid the need for
179 : // locks, since gCertVerificationThreadPool is only ever accessed on the socket
180 : // transport thread.
181 : void
182 1426 : InitializeSSLServerCertVerificationThreads()
183 : {
184 : // TODO: tuning, make parameters preferences
185 : // XXX: instantiate nsThreadPool directly, to make this more bulletproof.
186 : // Currently, the nsThreadPool.h header isn't exported for us to do so.
187 : nsresult rv = CallCreateInstance(NS_THREADPOOL_CONTRACTID,
188 1426 : &gCertVerificationThreadPool);
189 1426 : if (NS_FAILED(rv)) {
190 0 : NS_WARNING("Failed to create SSL cert verification threads.");
191 0 : return;
192 : }
193 :
194 1426 : (void) gCertVerificationThreadPool->SetIdleThreadLimit(5);
195 1426 : (void) gCertVerificationThreadPool->SetIdleThreadTimeout(30 * 1000);
196 1426 : (void) gCertVerificationThreadPool->SetThreadLimit(5);
197 : }
198 :
199 : // Called when the socket transport thread finishes, to destroy the thread
200 : // pool. Since the socket transport service has stopped processing events, it
201 : // will not attempt any more SSL I/O operations, so it is clearly safe to shut
202 : // down the SSL cert verification infrastructure. Also, the STS will not
203 : // dispatch many SSL verification result events at this point, so any pending
204 : // cert verifications will (correctly) fail at the point they are dispatched.
205 : //
206 : // The other shutdown race condition that is possible is a race condition with
207 : // shutdown of the nsNSSComponent service. We use the
208 : // nsNSSShutdownPreventionLock where needed (not here) to prevent that.
209 1426 : void StopSSLServerCertVerificationThreads()
210 : {
211 1426 : if (gCertVerificationThreadPool) {
212 1426 : gCertVerificationThreadPool->Shutdown();
213 1426 : NS_RELEASE(gCertVerificationThreadPool);
214 : }
215 1426 : }
216 :
217 : namespace {
218 :
219 : // Dispatched to the STS thread to notify the socketInfo of the verification
220 : // result.
221 : //
222 : // This will cause the PR_Poll in the STS thread to return, so things work
223 : // correctly even if the STS thread is blocked polling (only) on the file
224 : // descriptor that is waiting for this result.
225 : class SSLServerCertVerificationResult : public nsRunnable
226 16 : {
227 : public:
228 : NS_DECL_NSIRUNNABLE
229 :
230 : SSLServerCertVerificationResult(nsNSSSocketInfo & socketInfo,
231 : PRErrorCode errorCode,
232 : SSLErrorMessageType errorMessageType =
233 : PlainErrorMessage);
234 :
235 : void Dispatch();
236 : private:
237 : const nsRefPtr<nsNSSSocketInfo> mSocketInfo;
238 : public:
239 : const PRErrorCode mErrorCode;
240 : const SSLErrorMessageType mErrorMessageType;
241 : };
242 :
243 : class CertErrorRunnable : public SyncRunnableBase
244 0 : {
245 : public:
246 0 : CertErrorRunnable(const void * fdForLogging,
247 : nsIX509Cert * cert,
248 : nsNSSSocketInfo * infoObject,
249 : PRErrorCode defaultErrorCodeToReport,
250 : PRUint32 collectedErrors,
251 : PRErrorCode errorCodeTrust,
252 : PRErrorCode errorCodeMismatch,
253 : PRErrorCode errorCodeExpired)
254 : : mFdForLogging(fdForLogging), mCert(cert), mInfoObject(infoObject),
255 : mDefaultErrorCodeToReport(defaultErrorCodeToReport),
256 : mCollectedErrors(collectedErrors),
257 : mErrorCodeTrust(errorCodeTrust),
258 : mErrorCodeMismatch(errorCodeMismatch),
259 0 : mErrorCodeExpired(errorCodeExpired)
260 : {
261 0 : }
262 :
263 : virtual void RunOnTargetThread();
264 : nsRefPtr<SSLServerCertVerificationResult> mResult; // out
265 : private:
266 : SSLServerCertVerificationResult* CheckCertOverrides();
267 :
268 : const void * const mFdForLogging; // may become an invalid pointer; do not dereference
269 : const nsCOMPtr<nsIX509Cert> mCert;
270 : const nsRefPtr<nsNSSSocketInfo> mInfoObject;
271 : const PRErrorCode mDefaultErrorCodeToReport;
272 : const PRUint32 mCollectedErrors;
273 : const PRErrorCode mErrorCodeTrust;
274 : const PRErrorCode mErrorCodeMismatch;
275 : const PRErrorCode mErrorCodeExpired;
276 : };
277 :
278 : SSLServerCertVerificationResult *
279 0 : CertErrorRunnable::CheckCertOverrides()
280 : {
281 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p][%p] top of CheckCertOverrides\n",
282 : mFdForLogging, this));
283 :
284 0 : if (!NS_IsMainThread()) {
285 0 : NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
286 0 : return new SSLServerCertVerificationResult(*mInfoObject,
287 0 : mDefaultErrorCodeToReport);
288 : }
289 :
290 : PRInt32 port;
291 0 : mInfoObject->GetPort(&port);
292 :
293 0 : nsCString hostWithPortString;
294 0 : hostWithPortString.AppendASCII(mInfoObject->GetHostName());
295 0 : hostWithPortString.AppendLiteral(":");
296 0 : hostWithPortString.AppendInt(port);
297 :
298 0 : PRUint32 remaining_display_errors = mCollectedErrors;
299 :
300 : nsresult nsrv;
301 :
302 : // Enforce Strict-Transport-Security for hosts that are "STS" hosts:
303 : // connections must be dropped when there are any certificate errors
304 : // (STS Spec section 7.3).
305 0 : bool strictTransportSecurityEnabled = false;
306 : nsCOMPtr<nsIStrictTransportSecurityService> stss
307 0 : = do_GetService(NS_STSSERVICE_CONTRACTID, &nsrv);
308 0 : if (NS_SUCCEEDED(nsrv)) {
309 0 : nsrv = stss->IsStsHost(mInfoObject->GetHostName(),
310 0 : &strictTransportSecurityEnabled);
311 : }
312 0 : if (NS_FAILED(nsrv)) {
313 0 : return new SSLServerCertVerificationResult(*mInfoObject,
314 0 : mDefaultErrorCodeToReport);
315 : }
316 :
317 0 : if (!strictTransportSecurityEnabled) {
318 : nsCOMPtr<nsICertOverrideService> overrideService =
319 0 : do_GetService(NS_CERTOVERRIDE_CONTRACTID);
320 : // it is fine to continue without the nsICertOverrideService
321 :
322 0 : PRUint32 overrideBits = 0;
323 :
324 0 : if (overrideService)
325 : {
326 : bool haveOverride;
327 : bool isTemporaryOverride; // we don't care
328 0 : nsCString hostString(mInfoObject->GetHostName());
329 0 : nsrv = overrideService->HasMatchingOverride(hostString, port,
330 : mCert,
331 : &overrideBits,
332 : &isTemporaryOverride,
333 0 : &haveOverride);
334 0 : if (NS_SUCCEEDED(nsrv) && haveOverride)
335 : {
336 : // remove the errors that are already overriden
337 0 : remaining_display_errors -= overrideBits;
338 : }
339 : }
340 :
341 0 : if (!remaining_display_errors) {
342 : // all errors are covered by override rules, so let's accept the cert
343 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
344 : ("[%p][%p] All errors covered by override rules\n",
345 : mFdForLogging, this));
346 0 : return new SSLServerCertVerificationResult(*mInfoObject, 0);
347 : }
348 : } else {
349 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
350 : ("[%p][%p] Strict-Transport-Security is violated: untrusted "
351 : "transport layer\n", mFdForLogging, this));
352 : }
353 :
354 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
355 : ("[%p][%p] Certificate error was not overridden\n",
356 : mFdForLogging, this));
357 :
358 : // Ok, this is a full stop.
359 : // First, deliver the technical details of the broken SSL status.
360 :
361 : // Try to get a nsIBadCertListener2 implementation from the socket consumer.
362 0 : nsCOMPtr<nsIInterfaceRequestor> cb;
363 0 : mInfoObject->GetNotificationCallbacks(getter_AddRefs(cb));
364 0 : if (cb) {
365 0 : nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
366 0 : if (bcl) {
367 0 : nsIInterfaceRequestor *csi = static_cast<nsIInterfaceRequestor*>(mInfoObject);
368 0 : bool suppressMessage = false; // obsolete, ignored
369 0 : nsrv = bcl->NotifyCertProblem(csi, mInfoObject->SSLStatus(),
370 0 : hostWithPortString, &suppressMessage);
371 : }
372 : }
373 :
374 : nsCOMPtr<nsIRecentBadCertsService> recentBadCertsService =
375 0 : do_GetService(NS_RECENTBADCERTS_CONTRACTID);
376 :
377 0 : if (recentBadCertsService) {
378 0 : NS_ConvertUTF8toUTF16 hostWithPortStringUTF16(hostWithPortString);
379 0 : recentBadCertsService->AddBadCert(hostWithPortStringUTF16,
380 0 : mInfoObject->SSLStatus());
381 : }
382 :
383 : // pick the error code to report by priority
384 : PRErrorCode errorCodeToReport = mErrorCodeTrust ? mErrorCodeTrust
385 : : mErrorCodeMismatch ? mErrorCodeMismatch
386 : : mErrorCodeExpired ? mErrorCodeExpired
387 0 : : mDefaultErrorCodeToReport;
388 :
389 0 : return new SSLServerCertVerificationResult(*mInfoObject, errorCodeToReport,
390 0 : OverridableCertErrorMessage);
391 : }
392 :
393 : void
394 0 : CertErrorRunnable::RunOnTargetThread()
395 : {
396 0 : MOZ_ASSERT(NS_IsMainThread());
397 :
398 0 : mResult = CheckCertOverrides();
399 :
400 0 : MOZ_ASSERT(mResult);
401 0 : }
402 :
403 : // Returns null with the error code (PR_GetError()) set if it does not create
404 : // the CertErrorRunnable.
405 : CertErrorRunnable *
406 0 : CreateCertErrorRunnable(PRErrorCode defaultErrorCodeToReport,
407 : nsNSSSocketInfo * socketInfo,
408 : CERTCertificate * cert,
409 : const void * fdForLogging)
410 : {
411 0 : MOZ_ASSERT(socketInfo);
412 0 : MOZ_ASSERT(cert);
413 :
414 : // cert was revoked, don't do anything else
415 0 : if (defaultErrorCodeToReport == SEC_ERROR_REVOKED_CERTIFICATE) {
416 0 : PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
417 0 : return nsnull;
418 : }
419 :
420 0 : if (defaultErrorCodeToReport == 0) {
421 0 : NS_ERROR("No error code set during certificate validation failure.");
422 0 : PR_SetError(PR_INVALID_STATE_ERROR, 0);
423 0 : return nsnull;
424 : }
425 :
426 0 : nsRefPtr<nsNSSCertificate> nssCert;
427 0 : nssCert = nsNSSCertificate::Create(cert);
428 0 : if (!nssCert) {
429 0 : NS_ERROR("nsNSSCertificate::Create failed");
430 0 : PR_SetError(SEC_ERROR_NO_MEMORY, 0);
431 0 : return nsnull;
432 : }
433 :
434 : SECStatus srv;
435 : nsresult nsrv;
436 :
437 0 : nsCOMPtr<nsINSSComponent> inss = do_GetService(kNSSComponentCID, &nsrv);
438 0 : if (!inss) {
439 0 : NS_ERROR("do_GetService(kNSSComponentCID) failed");
440 0 : PR_SetError(defaultErrorCodeToReport, 0);
441 0 : return nsnull;
442 : }
443 :
444 0 : nsRefPtr<nsCERTValInParamWrapper> survivingParams;
445 0 : nsrv = inss->GetDefaultCERTValInParam(survivingParams);
446 0 : if (NS_FAILED(nsrv)) {
447 0 : NS_ERROR("GetDefaultCERTValInParam failed");
448 0 : PR_SetError(defaultErrorCodeToReport, 0);
449 0 : return nsnull;
450 : }
451 :
452 0 : PRArenaPool *log_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
453 0 : PRArenaPoolCleanerFalseParam log_arena_cleaner(log_arena);
454 0 : if (!log_arena) {
455 0 : NS_ERROR("PORT_NewArena failed");
456 0 : return nsnull; // PORT_NewArena set error code
457 : }
458 :
459 0 : CERTVerifyLog *verify_log = PORT_ArenaZNew(log_arena, CERTVerifyLog);
460 0 : if (!verify_log) {
461 0 : NS_ERROR("PORT_ArenaZNew failed");
462 0 : return nsnull; // PORT_ArenaZNew set error code
463 : }
464 0 : CERTVerifyLogContentsCleaner verify_log_cleaner(verify_log);
465 0 : verify_log->arena = log_arena;
466 :
467 0 : if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
468 : srv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), cert,
469 : true, certificateUsageSSLServer,
470 : PR_Now(), static_cast<void*>(socketInfo),
471 0 : verify_log, NULL);
472 : }
473 : else {
474 : CERTValOutParam cvout[2];
475 0 : cvout[0].type = cert_po_errorLog;
476 0 : cvout[0].value.pointer.log = verify_log;
477 0 : cvout[1].type = cert_po_end;
478 :
479 : srv = CERT_PKIXVerifyCert(cert, certificateUsageSSLServer,
480 : survivingParams->GetRawPointerForNSS(),
481 0 : cvout, static_cast<void*>(socketInfo));
482 : }
483 :
484 : // We ignore the result code of the cert verification.
485 : // Either it is a failure, which is expected, and we'll process the
486 : // verify log below.
487 : // Or it is a success, then a domain mismatch is the only
488 : // possible failure.
489 :
490 0 : PRErrorCode errorCodeMismatch = 0;
491 0 : PRErrorCode errorCodeTrust = 0;
492 0 : PRErrorCode errorCodeExpired = 0;
493 :
494 0 : PRUint32 collected_errors = 0;
495 :
496 0 : if (socketInfo->IsCertIssuerBlacklisted()) {
497 0 : collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
498 0 : errorCodeTrust = defaultErrorCodeToReport;
499 : }
500 :
501 : // Check the name field against the desired hostname.
502 0 : if (CERT_VerifyCertName(cert, socketInfo->GetHostName()) != SECSuccess) {
503 0 : collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
504 0 : errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
505 : }
506 :
507 : CERTVerifyLogNode *i_node;
508 0 : for (i_node = verify_log->head; i_node; i_node = i_node->next)
509 : {
510 0 : switch (i_node->error)
511 : {
512 : case SEC_ERROR_UNKNOWN_ISSUER:
513 : case SEC_ERROR_CA_CERT_INVALID:
514 : case SEC_ERROR_UNTRUSTED_ISSUER:
515 : case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
516 : case SEC_ERROR_UNTRUSTED_CERT:
517 : case SEC_ERROR_INADEQUATE_KEY_USAGE:
518 : // We group all these errors as "cert not trusted"
519 0 : collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
520 0 : if (errorCodeTrust == SECSuccess) {
521 0 : errorCodeTrust = i_node->error;
522 : }
523 0 : break;
524 : case SSL_ERROR_BAD_CERT_DOMAIN:
525 0 : collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
526 0 : if (errorCodeMismatch == SECSuccess) {
527 0 : errorCodeMismatch = i_node->error;
528 : }
529 0 : break;
530 : case SEC_ERROR_EXPIRED_CERTIFICATE:
531 0 : collected_errors |= nsICertOverrideService::ERROR_TIME;
532 0 : if (errorCodeExpired == SECSuccess) {
533 0 : errorCodeExpired = i_node->error;
534 : }
535 0 : break;
536 : default:
537 0 : PR_SetError(i_node->error, 0);
538 0 : return nsnull;
539 : }
540 : }
541 :
542 0 : if (!collected_errors)
543 : {
544 : // This will happen when CERT_*Verify* only returned error(s) that are
545 : // not on our whitelist of overridable certificate errors.
546 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] !collected_errors: %d\n",
547 : fdForLogging, static_cast<int>(defaultErrorCodeToReport)));
548 0 : PR_SetError(defaultErrorCodeToReport, 0);
549 0 : return nsnull;
550 : }
551 :
552 0 : socketInfo->SetStatusErrorBits(*nssCert, collected_errors);
553 :
554 : return new CertErrorRunnable(fdForLogging,
555 0 : static_cast<nsIX509Cert*>(nssCert.get()),
556 : socketInfo, defaultErrorCodeToReport,
557 : collected_errors, errorCodeTrust,
558 0 : errorCodeMismatch, errorCodeExpired);
559 : }
560 :
561 : // When doing async cert processing, we dispatch one of these runnables to the
562 : // socket transport service thread, which blocks the socket transport
563 : // service thread while it waits for the inner CertErrorRunnable to execute
564 : // CheckCertOverrides on the main thread. CheckCertOverrides must block events
565 : // on both of these threads because it calls nsNSSSocketInfo::GetInterface(),
566 : // which may call nsHttpConnection::GetInterface() through
567 : // nsNSSSocketInfo::mCallbacks. nsHttpConnection::GetInterface must always
568 : // execute on the main thread, with the socket transport service thread
569 : // blocked.
570 : class CertErrorRunnableRunnable : public nsRunnable
571 0 : {
572 : public:
573 0 : CertErrorRunnableRunnable(CertErrorRunnable * certErrorRunnable)
574 0 : : mCertErrorRunnable(certErrorRunnable)
575 : {
576 0 : }
577 : private:
578 0 : NS_IMETHOD Run()
579 : {
580 0 : nsresult rv = mCertErrorRunnable->DispatchToMainThreadAndWait();
581 : // The result must run on the socket transport thread, which we are already
582 : // on, so we can just run it directly, instead of dispatching it.
583 0 : if (NS_SUCCEEDED(rv)) {
584 0 : rv = mCertErrorRunnable->mResult ? mCertErrorRunnable->mResult->Run()
585 0 : : NS_ERROR_UNEXPECTED;
586 : }
587 0 : return rv;
588 : }
589 : nsRefPtr<CertErrorRunnable> mCertErrorRunnable;
590 : };
591 :
592 : class SSLServerCertVerificationJob : public nsRunnable
593 : {
594 : public:
595 : // Must be called only on the socket transport thread
596 : static SECStatus Dispatch(const void * fdForLogging,
597 : nsNSSSocketInfo * infoObject,
598 : CERTCertificate * serverCert);
599 : private:
600 : NS_DECL_NSIRUNNABLE
601 :
602 : // Must be called only on the socket transport thread
603 : SSLServerCertVerificationJob(const void * fdForLogging,
604 : nsNSSSocketInfo & socketInfo,
605 : CERTCertificate & cert);
606 : ~SSLServerCertVerificationJob();
607 :
608 : const void * const mFdForLogging;
609 : const nsRefPtr<nsNSSSocketInfo> mSocketInfo;
610 : CERTCertificate * const mCert;
611 : };
612 :
613 4 : SSLServerCertVerificationJob::SSLServerCertVerificationJob(
614 : const void * fdForLogging, nsNSSSocketInfo & socketInfo,
615 : CERTCertificate & cert)
616 : : mFdForLogging(fdForLogging)
617 : , mSocketInfo(&socketInfo)
618 4 : , mCert(CERT_DupCertificate(&cert))
619 : {
620 4 : }
621 :
622 12 : SSLServerCertVerificationJob::~SSLServerCertVerificationJob()
623 : {
624 4 : CERT_DestroyCertificate(mCert);
625 16 : }
626 :
627 : SECStatus
628 4 : PSM_SSL_PKIX_AuthCertificate(CERTCertificate *peerCert, void * pinarg,
629 : const char * hostname)
630 : {
631 : SECStatus rv;
632 :
633 4 : if (!nsNSSComponent::globalConstFlagUsePKIXVerification) {
634 : rv = CERT_VerifyCertNow(CERT_GetDefaultCertDB(), peerCert, true,
635 4 : certUsageSSLServer, pinarg);
636 : }
637 : else {
638 : nsresult nsrv;
639 0 : nsCOMPtr<nsINSSComponent> inss = do_GetService(kNSSComponentCID, &nsrv);
640 0 : if (!inss)
641 0 : return SECFailure;
642 0 : nsRefPtr<nsCERTValInParamWrapper> survivingParams;
643 0 : if (NS_FAILED(inss->GetDefaultCERTValInParam(survivingParams)))
644 0 : return SECFailure;
645 :
646 : CERTValOutParam cvout[1];
647 0 : cvout[0].type = cert_po_end;
648 :
649 : rv = CERT_PKIXVerifyCert(peerCert, certificateUsageSSLServer,
650 : survivingParams->GetRawPointerForNSS(),
651 0 : cvout, pinarg);
652 : }
653 :
654 4 : if (rv == SECSuccess) {
655 : /* cert is OK. This is the client side of an SSL connection.
656 : * Now check the name field in the cert against the desired hostname.
657 : * NB: This is our only defense against Man-In-The-Middle (MITM) attacks!
658 : */
659 4 : if (hostname && hostname[0])
660 4 : rv = CERT_VerifyCertName(peerCert, hostname);
661 : else
662 0 : rv = SECFailure;
663 4 : if (rv != SECSuccess)
664 0 : PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
665 : }
666 :
667 4 : return rv;
668 : }
669 :
670 : struct nsSerialBinaryBlacklistEntry
671 : {
672 : unsigned int len;
673 : const char *binary_serial;
674 : };
675 :
676 : // bug 642395
677 : static struct nsSerialBinaryBlacklistEntry myUTNBlacklistEntries[] = {
678 : { 17, "\x00\x92\x39\xd5\x34\x8f\x40\xd1\x69\x5a\x74\x54\x70\xe1\xf2\x3f\x43" },
679 : { 17, "\x00\xd8\xf3\x5f\x4e\xb7\x87\x2b\x2d\xab\x06\x92\xe3\x15\x38\x2f\xb0" },
680 : { 16, "\x72\x03\x21\x05\xc5\x0c\x08\x57\x3d\x8e\xa5\x30\x4e\xfe\xe8\xb0" },
681 : { 17, "\x00\xb0\xb7\x13\x3e\xd0\x96\xf9\xb5\x6f\xae\x91\xc8\x74\xbd\x3a\xc0" },
682 : { 16, "\x39\x2a\x43\x4f\x0e\x07\xdf\x1f\x8a\xa3\x05\xde\x34\xe0\xc2\x29" },
683 : { 16, "\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" },
684 : { 17, "\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" },
685 : { 17, "\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" },
686 : { 16, "\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" },
687 : { 17, "\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" },
688 : { 0, 0 } // end marker
689 : };
690 :
691 : // Call this if we have already decided that a cert should be treated as INVALID,
692 : // in order to check if we to worsen the error to REVOKED.
693 : PRErrorCode
694 0 : PSM_SSL_DigiNotarTreatAsRevoked(CERTCertificate * serverCert,
695 : CERTCertList * serverCertChain)
696 : {
697 : // If any involved cert was issued by DigiNotar,
698 : // and serverCert was issued after 01-JUL-2011,
699 : // then worsen the error to revoked.
700 :
701 0 : PRTime cutoff = 0;
702 0 : PRStatus status = PR_ParseTimeString("01-JUL-2011 00:00", true, &cutoff);
703 0 : if (status != PR_SUCCESS) {
704 0 : NS_ASSERTION(status == PR_SUCCESS, "PR_ParseTimeString failed");
705 : // be safe, assume it's afterwards, keep going
706 : } else {
707 0 : PRTime notBefore = 0, notAfter = 0;
708 0 : if (CERT_GetCertTimes(serverCert, ¬Before, ¬After) == SECSuccess &&
709 : notBefore < cutoff) {
710 : // no worsening for certs issued before the cutoff date
711 0 : return 0;
712 : }
713 : }
714 :
715 0 : for (CERTCertListNode *node = CERT_LIST_HEAD(serverCertChain);
716 0 : !CERT_LIST_END(node, serverCertChain);
717 : node = CERT_LIST_NEXT(node)) {
718 0 : if (node->cert->issuerName &&
719 0 : strstr(node->cert->issuerName, "CN=DigiNotar")) {
720 0 : return SEC_ERROR_REVOKED_CERTIFICATE;
721 : }
722 : }
723 :
724 0 : return 0;
725 : }
726 :
727 : // Call this only if a cert has been reported by NSS as VALID
728 : PRErrorCode
729 4 : PSM_SSL_BlacklistDigiNotar(CERTCertificate * serverCert,
730 : CERTCertList * serverCertChain)
731 : {
732 4 : bool isDigiNotarIssuedCert = false;
733 :
734 32 : for (CERTCertListNode *node = CERT_LIST_HEAD(serverCertChain);
735 16 : !CERT_LIST_END(node, serverCertChain);
736 : node = CERT_LIST_NEXT(node)) {
737 12 : if (!node->cert->issuerName)
738 0 : continue;
739 :
740 12 : if (strstr(node->cert->issuerName, "CN=DigiNotar")) {
741 0 : isDigiNotarIssuedCert = true;
742 : }
743 : }
744 :
745 4 : if (isDigiNotarIssuedCert) {
746 : // let's see if we want to worsen the error code to revoked.
747 0 : PRErrorCode revoked_code = PSM_SSL_DigiNotarTreatAsRevoked(serverCert, serverCertChain);
748 0 : return (revoked_code != 0) ? revoked_code : SEC_ERROR_UNTRUSTED_ISSUER;
749 : }
750 :
751 4 : return 0;
752 : }
753 :
754 : // This function assumes that we will only use the SPDY connection coalescing
755 : // feature on connections where we have negotiated SPDY using NPN. If we ever
756 : // talk SPDY without having negotiated it with SPDY, this code will give wrong
757 : // and perhaps unsafe results.
758 : //
759 : // Returns SECSuccess on the initial handshake of all connections, on
760 : // renegotiations for any connections where we did not negotiate SPDY, or on any
761 : // SPDY connection where the server's certificate did not change.
762 : //
763 : // Prohibit changing the server cert only if we negotiated SPDY,
764 : // in order to support SPDY's cross-origin connection pooling.
765 :
766 : static SECStatus
767 4 : BlockServerCertChangeForSpdy(nsNSSSocketInfo *infoObject,
768 : CERTCertificate *serverCert)
769 : {
770 : // Get the existing cert. If there isn't one, then there is
771 : // no cert change to worry about.
772 8 : nsCOMPtr<nsIX509Cert> cert;
773 8 : nsCOMPtr<nsIX509Cert2> cert2;
774 :
775 8 : nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
776 4 : if (!status) {
777 : // If we didn't have a status, then this is the
778 : // first handshake on this connection, not a
779 : // renegotiation.
780 4 : return SECSuccess;
781 : }
782 :
783 0 : status->GetServerCert(getter_AddRefs(cert));
784 0 : cert2 = do_QueryInterface(cert);
785 0 : if (!cert2) {
786 : NS_NOTREACHED("every nsSSLStatus must have a cert"
787 0 : "that implements nsIX509Cert2");
788 0 : PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
789 0 : return SECFailure;
790 : }
791 :
792 : // Filter out sockets that did not neogtiate SPDY via NPN
793 0 : nsCAutoString negotiatedNPN;
794 0 : nsresult rv = infoObject->GetNegotiatedNPN(negotiatedNPN);
795 0 : NS_ASSERTION(NS_SUCCEEDED(rv),
796 : "GetNegotiatedNPN() failed during renegotiation");
797 :
798 0 : if (NS_SUCCEEDED(rv) && !negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2")))
799 0 : return SECSuccess;
800 :
801 : // If GetNegotiatedNPN() failed we will assume spdy for safety's safe
802 0 : if (NS_FAILED(rv))
803 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
804 : ("BlockServerCertChangeForSpdy failed GetNegotiatedNPN() call."
805 : " Assuming spdy.\n"));
806 :
807 : // Check to see if the cert has actually changed
808 0 : CERTCertificate * c = cert2->GetCert();
809 0 : NS_ASSERTION(c, "very bad and hopefully impossible state");
810 0 : bool sameCert = CERT_CompareCerts(c, serverCert);
811 0 : CERT_DestroyCertificate(c);
812 0 : if (sameCert)
813 0 : return SECSuccess;
814 :
815 : // Report an error - changed cert is confirmed
816 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
817 : ("SPDY Refused to allow new cert during renegotiation\n"));
818 0 : PR_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, 0);
819 0 : return SECFailure;
820 : }
821 :
822 : SECStatus
823 4 : AuthCertificate(nsNSSSocketInfo * socketInfo, CERTCertificate * cert)
824 : {
825 4 : if (BlockServerCertChangeForSpdy(socketInfo, cert) != SECSuccess)
826 0 : return SECFailure;
827 :
828 8 : if (cert->serialNumber.data &&
829 : cert->issuerName &&
830 : !strcmp(cert->issuerName,
831 4 : "CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US")) {
832 :
833 0 : unsigned char *server_cert_comparison_start = cert->serialNumber.data;
834 0 : unsigned int server_cert_comparison_len = cert->serialNumber.len;
835 :
836 0 : while (server_cert_comparison_len) {
837 0 : if (*server_cert_comparison_start != 0)
838 0 : break;
839 :
840 0 : ++server_cert_comparison_start;
841 0 : --server_cert_comparison_len;
842 : }
843 :
844 0 : nsSerialBinaryBlacklistEntry *walk = myUTNBlacklistEntries;
845 0 : for ( ; walk && walk->len; ++walk) {
846 :
847 0 : unsigned char *locked_cert_comparison_start = (unsigned char*)walk->binary_serial;
848 0 : unsigned int locked_cert_comparison_len = walk->len;
849 :
850 0 : while (locked_cert_comparison_len) {
851 0 : if (*locked_cert_comparison_start != 0)
852 0 : break;
853 :
854 0 : ++locked_cert_comparison_start;
855 0 : --locked_cert_comparison_len;
856 : }
857 :
858 0 : if (server_cert_comparison_len == locked_cert_comparison_len &&
859 0 : !memcmp(server_cert_comparison_start, locked_cert_comparison_start, locked_cert_comparison_len)) {
860 0 : PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
861 0 : return SECFailure;
862 : }
863 : }
864 : }
865 :
866 : SECStatus rv = PSM_SSL_PKIX_AuthCertificate(cert, socketInfo,
867 4 : socketInfo->GetHostName());
868 :
869 : // We want to remember the CA certs in the temp db, so that the application can find the
870 : // complete chain at any time it might need it.
871 : // But we keep only those CA certs in the temp db, that we didn't already know.
872 :
873 8 : nsRefPtr<nsSSLStatus> status = socketInfo->SSLStatus();
874 8 : nsRefPtr<nsNSSCertificate> nsc;
875 :
876 4 : if (!status || !status->mServerCert) {
877 4 : nsc = nsNSSCertificate::Create(cert);
878 : }
879 :
880 4 : CERTCertList *certList = nsnull;
881 4 : certList = CERT_GetCertChainFromCert(cert, PR_Now(), certUsageSSLCA);
882 4 : if (!certList) {
883 0 : rv = SECFailure;
884 : } else {
885 : PRErrorCode blacklistErrorCode;
886 4 : if (rv == SECSuccess) { // PSM_SSL_PKIX_AuthCertificate said "valid cert"
887 4 : blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(cert, certList);
888 : } else { // PSM_SSL_PKIX_AuthCertificate said "invalid cert"
889 0 : PRErrorCode savedErrorCode = PORT_GetError();
890 : // Check if we want to worsen the error code to "revoked".
891 0 : blacklistErrorCode = PSM_SSL_DigiNotarTreatAsRevoked(cert, certList);
892 0 : if (blacklistErrorCode == 0) {
893 : // we don't worsen the code, let's keep the original error code from NSS
894 0 : PORT_SetError(savedErrorCode);
895 : }
896 : }
897 :
898 4 : if (blacklistErrorCode != 0) {
899 0 : socketInfo->SetCertIssuerBlacklisted();
900 0 : PORT_SetError(blacklistErrorCode);
901 0 : rv = SECFailure;
902 : }
903 : }
904 :
905 4 : if (rv == SECSuccess) {
906 4 : if (nsc) {
907 : bool dummyIsEV;
908 4 : nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
909 : }
910 :
911 8 : nsCOMPtr<nsINSSComponent> nssComponent;
912 :
913 32 : for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
914 16 : !CERT_LIST_END(node, certList);
915 : node = CERT_LIST_NEXT(node)) {
916 :
917 12 : if (node->cert->slot) {
918 : // This cert was found on a token, no need to remember it in the temp db.
919 4 : continue;
920 : }
921 :
922 8 : if (node->cert->isperm) {
923 : // We don't need to remember certs already stored in perm db.
924 0 : continue;
925 : }
926 :
927 8 : if (node->cert == cert) {
928 : // We don't want to remember the server cert,
929 : // the code that cares for displaying page info does this already.
930 4 : continue;
931 : }
932 :
933 : // We have found a signer cert that we want to remember.
934 4 : char* nickname = nsNSSCertificate::defaultServerNickname(node->cert);
935 4 : if (nickname && *nickname) {
936 4 : PK11SlotInfo *slot = PK11_GetInternalKeySlot();
937 4 : if (slot) {
938 : PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE,
939 4 : nickname, false);
940 4 : PK11_FreeSlot(slot);
941 : }
942 : }
943 4 : PR_FREEIF(nickname);
944 : }
945 :
946 4 : if (certList) {
947 4 : CERT_DestroyCertList(certList);
948 : }
949 :
950 : // The connection may get terminated, for example, if the server requires
951 : // a client cert. Let's provide a minimal SSLStatus
952 : // to the caller that contains at least the cert and its status.
953 4 : if (!status) {
954 4 : status = new nsSSLStatus();
955 4 : socketInfo->SetSSLStatus(status);
956 : }
957 :
958 4 : if (rv == SECSuccess) {
959 : // Certificate verification succeeded delete any potential record
960 : // of certificate error bits.
961 : nsSSLIOLayerHelpers::mHostsWithCertErrors->RememberCertHasError(
962 4 : socketInfo, nsnull, rv);
963 : }
964 : else {
965 : // Certificate verification failed, update the status' bits.
966 : nsSSLIOLayerHelpers::mHostsWithCertErrors->LookupCertErrorBits(
967 0 : socketInfo, status);
968 : }
969 :
970 4 : if (status && !status->mServerCert) {
971 4 : status->mServerCert = nsc;
972 4 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
973 : ("AuthCertificate setting NEW cert %p\n", status->mServerCert.get()));
974 : }
975 : }
976 :
977 4 : return rv;
978 : }
979 :
980 : /*static*/ SECStatus
981 4 : SSLServerCertVerificationJob::Dispatch(const void * fdForLogging,
982 : nsNSSSocketInfo * socketInfo,
983 : CERTCertificate * serverCert)
984 : {
985 : // Runs on the socket transport thread
986 4 : if (!socketInfo || !serverCert) {
987 0 : NS_ERROR("Invalid parameters for SSL server cert validation");
988 0 : PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
989 0 : return SECFailure;
990 : }
991 :
992 : nsRefPtr<SSLServerCertVerificationJob> job
993 8 : = new SSLServerCertVerificationJob(fdForLogging, *socketInfo, *serverCert);
994 :
995 4 : socketInfo->SetCertVerificationWaiting();
996 : nsresult nrv;
997 4 : if (!gCertVerificationThreadPool) {
998 0 : nrv = NS_ERROR_NOT_INITIALIZED;
999 : } else {
1000 4 : nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL);
1001 : }
1002 4 : if (NS_FAILED(nrv)) {
1003 : // We can't call SetCertVerificationResult here to change
1004 : // mCertVerificationState because SetCertVerificationResult will call
1005 : // libssl functions that acquire SSL locks that are already being held at
1006 : // this point. socketInfo->mCertVerificationState will be stuck at
1007 : // waiting_for_cert_verification here, but that is OK because we already
1008 : // have to be able to handle cases where we encounter non-cert errors while
1009 : // in that state.
1010 : PRErrorCode error = nrv == NS_ERROR_OUT_OF_MEMORY
1011 : ? SEC_ERROR_NO_MEMORY
1012 0 : : PR_INVALID_STATE_ERROR;
1013 0 : PORT_SetError(error);
1014 0 : return SECFailure;
1015 : }
1016 :
1017 4 : PORT_SetError(PR_WOULD_BLOCK_ERROR);
1018 4 : return SECWouldBlock;
1019 : }
1020 :
1021 : NS_IMETHODIMP
1022 4 : SSLServerCertVerificationJob::Run()
1023 : {
1024 : // Runs on a cert verification thread
1025 :
1026 4 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
1027 : ("[%p] SSLServerCertVerificationJob::Run\n", mSocketInfo.get()));
1028 :
1029 : PRErrorCode error;
1030 :
1031 8 : nsNSSShutDownPreventionLock nssShutdownPrevention;
1032 4 : if (mSocketInfo->isAlreadyShutDown()) {
1033 0 : error = SEC_ERROR_USER_CANCELLED;
1034 : } else {
1035 : // Reset the error code here so we can detect if AuthCertificate fails to
1036 : // set the error code if/when it fails.
1037 4 : PR_SetError(0, 0);
1038 4 : SECStatus rv = AuthCertificate(mSocketInfo, mCert);
1039 4 : if (rv == SECSuccess) {
1040 : nsRefPtr<SSLServerCertVerificationResult> restart
1041 12 : = new SSLServerCertVerificationResult(*mSocketInfo, 0);
1042 4 : restart->Dispatch();
1043 4 : return NS_OK;
1044 : }
1045 :
1046 0 : error = PR_GetError();
1047 0 : if (error != 0) {
1048 : nsRefPtr<CertErrorRunnable> runnable = CreateCertErrorRunnable(
1049 0 : error, mSocketInfo, mCert, mFdForLogging);
1050 0 : if (!runnable) {
1051 : // CreateCertErrorRunnable set a new error code
1052 0 : error = PR_GetError();
1053 : } else {
1054 : // We must block the the socket transport service thread while the
1055 : // main thread executes the CertErrorRunnable. The CertErrorRunnable
1056 : // will dispatch the result asynchronously, so we don't have to block
1057 : // this thread waiting for it.
1058 :
1059 0 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
1060 : ("[%p][%p] Before dispatching CertErrorRunnable\n",
1061 : mFdForLogging, runnable.get()));
1062 :
1063 : nsresult nrv;
1064 : nsCOMPtr<nsIEventTarget> stsTarget
1065 0 : = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
1066 0 : if (NS_SUCCEEDED(nrv)) {
1067 0 : nrv = stsTarget->Dispatch(new CertErrorRunnableRunnable(runnable),
1068 0 : NS_DISPATCH_NORMAL);
1069 : }
1070 0 : if (NS_SUCCEEDED(nrv)) {
1071 0 : return NS_OK;
1072 : }
1073 :
1074 0 : NS_ERROR("Failed to dispatch CertErrorRunnable");
1075 0 : error = PR_INVALID_STATE_ERROR;
1076 : }
1077 : }
1078 : }
1079 :
1080 0 : if (error == 0) {
1081 0 : NS_NOTREACHED("no error set during certificate validation failure");
1082 0 : error = PR_INVALID_STATE_ERROR;
1083 : }
1084 :
1085 : nsRefPtr<SSLServerCertVerificationResult> failure
1086 0 : = new SSLServerCertVerificationResult(*mSocketInfo, error);
1087 0 : failure->Dispatch();
1088 0 : return NS_OK;
1089 : }
1090 :
1091 : } // unnamed namespace
1092 :
1093 : // Extracts whatever information we need out of fd (using SSL_*) and passes it
1094 : // to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should
1095 : // never do anything with fd except logging.
1096 : SECStatus
1097 4 : AuthCertificateHook(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
1098 : {
1099 : // Runs on the socket transport thread
1100 :
1101 4 : PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
1102 : ("[%p] starting AuthCertificateHook\n", fd));
1103 :
1104 : // Modern libssl always passes PR_TRUE for checkSig, and we have no means of
1105 : // doing verification without checking signatures.
1106 4 : NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
1107 :
1108 : // PSM never causes libssl to call this function with PR_TRUE for isServer,
1109 : // and many things in PSM assume that we are a client.
1110 4 : NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
1111 :
1112 4 : if (!checkSig || isServer) {
1113 0 : PR_SetError(PR_INVALID_STATE_ERROR, 0);
1114 0 : return SECFailure;
1115 : }
1116 :
1117 4 : CERTCertificate *serverCert = SSL_PeerCertificate(fd);
1118 8 : CERTCertificateCleaner serverCertCleaner(serverCert);
1119 :
1120 4 : nsNSSSocketInfo *socketInfo = static_cast<nsNSSSocketInfo*>(arg);
1121 :
1122 : bool onSTSThread;
1123 : nsresult nrv;
1124 : nsCOMPtr<nsIEventTarget> sts
1125 8 : = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
1126 4 : if (NS_SUCCEEDED(nrv)) {
1127 4 : nrv = sts->IsOnCurrentThread(&onSTSThread);
1128 : }
1129 :
1130 4 : if (NS_FAILED(nrv)) {
1131 0 : NS_ERROR("Could not get STS service or IsOnCurrentThread failed");
1132 0 : PR_SetError(PR_UNKNOWN_ERROR, 0);
1133 0 : return SECFailure;
1134 : }
1135 :
1136 4 : if (onSTSThread) {
1137 : // We *must* do certificate verification on a background thread because
1138 : // we need the socket transport thread to be free for our OCSP requests,
1139 : // and we *want* to do certificate verification on a background thread
1140 : // because of the performance benefits of doing so.
1141 : SECStatus rv = SSLServerCertVerificationJob::Dispatch(
1142 4 : static_cast<const void *>(fd), socketInfo, serverCert);
1143 4 : return rv;
1144 : }
1145 :
1146 : // We can't do certificate verification on a background thread, because the
1147 : // thread doing the network I/O may not interrupt its network I/O on receipt
1148 : // of our SSLServerCertVerificationResult event, and/or it might not even be
1149 : // a non-blocking socket.
1150 0 : SECStatus rv = AuthCertificate(socketInfo, serverCert);
1151 0 : if (rv == SECSuccess) {
1152 0 : return SECSuccess;
1153 : }
1154 :
1155 0 : PRErrorCode error = PR_GetError();
1156 0 : if (error != 0) {
1157 : nsRefPtr<CertErrorRunnable> runnable = CreateCertErrorRunnable(
1158 : error, socketInfo, serverCert,
1159 0 : static_cast<const void *>(fd));
1160 0 : if (!runnable) {
1161 : // CreateCertErrorRunnable sets a new error code when it fails
1162 0 : error = PR_GetError();
1163 : } else {
1164 : // We have to return SECSuccess or SECFailure based on the result of the
1165 : // override processing, so we must block this thread waiting for it. The
1166 : // CertErrorRunnable will NOT dispatch the result at all, since we passed
1167 : // false for CreateCertErrorRunnable's async parameter
1168 0 : nrv = runnable->DispatchToMainThreadAndWait();
1169 0 : if (NS_FAILED(nrv)) {
1170 0 : NS_ERROR("Failed to dispatch CertErrorRunnable");
1171 0 : PR_SetError(PR_INVALID_STATE_ERROR, 0);
1172 0 : return SECFailure;
1173 : }
1174 :
1175 0 : if (!runnable->mResult) {
1176 0 : NS_ERROR("CertErrorRunnable did not set result");
1177 0 : PR_SetError(PR_INVALID_STATE_ERROR, 0);
1178 0 : return SECFailure;
1179 : }
1180 :
1181 0 : if (runnable->mResult->mErrorCode == 0) {
1182 0 : return SECSuccess; // cert error override occurred.
1183 : }
1184 :
1185 : // We must call SetCanceled here to set the error message type
1186 : // in case it isn't PlainErrorMessage, which is what we would
1187 : // default to if we just called
1188 : // PR_SetError(runnable->mResult->mErrorCode, 0) and returned
1189 : // SECFailure without doing this.
1190 0 : socketInfo->SetCanceled(runnable->mResult->mErrorCode,
1191 0 : runnable->mResult->mErrorMessageType);
1192 0 : error = runnable->mResult->mErrorCode;
1193 : }
1194 : }
1195 :
1196 0 : if (error == 0) {
1197 0 : NS_ERROR("error code not set");
1198 0 : error = PR_UNKNOWN_ERROR;
1199 : }
1200 :
1201 0 : PR_SetError(error, 0);
1202 0 : return SECFailure;
1203 : }
1204 :
1205 4 : SSLServerCertVerificationResult::SSLServerCertVerificationResult(
1206 : nsNSSSocketInfo & socketInfo, PRErrorCode errorCode,
1207 : SSLErrorMessageType errorMessageType)
1208 : : mSocketInfo(&socketInfo)
1209 : , mErrorCode(errorCode)
1210 4 : , mErrorMessageType(errorMessageType)
1211 : {
1212 4 : }
1213 :
1214 : void
1215 4 : SSLServerCertVerificationResult::Dispatch()
1216 : {
1217 : nsresult rv;
1218 : nsCOMPtr<nsIEventTarget> stsTarget
1219 8 : = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1220 4 : NS_ASSERTION(stsTarget,
1221 : "Failed to get socket transport service event target");
1222 4 : rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL);
1223 4 : NS_ASSERTION(NS_SUCCEEDED(rv),
1224 : "Failed to dispatch SSLServerCertVerificationResult");
1225 4 : }
1226 :
1227 : NS_IMETHODIMP
1228 4 : SSLServerCertVerificationResult::Run()
1229 : {
1230 : // TODO: Assert that we're on the socket transport thread
1231 4 : mSocketInfo->SetCertVerificationResult(mErrorCode, mErrorMessageType);
1232 4 : return NS_OK;
1233 : }
1234 :
1235 : } } // namespace mozilla::psm
|