nss_ocsp.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "net/ocsp/nss_ocsp.h" 6 7#include <certt.h> 8#include <certdb.h> 9#include <ocsp.h> 10#include <nspr.h> 11#include <nss.h> 12#include <pthread.h> 13#include <secerr.h> 14 15#include <algorithm> 16#include <string> 17 18#include "base/basictypes.h" 19#include "base/callback.h" 20#include "base/compiler_specific.h" 21#include "base/lazy_instance.h" 22#include "base/logging.h" 23#include "base/message_loop.h" 24#include "base/metrics/histogram.h" 25#include "base/stl_util.h" 26#include "base/string_util.h" 27#include "base/stringprintf.h" 28#include "base/synchronization/condition_variable.h" 29#include "base/synchronization/lock.h" 30#include "base/threading/thread_checker.h" 31#include "base/time.h" 32#include "googleurl/src/gurl.h" 33#include "net/base/host_port_pair.h" 34#include "net/base/io_buffer.h" 35#include "net/base/load_flags.h" 36#include "net/http/http_request_headers.h" 37#include "net/http/http_response_headers.h" 38#include "net/url_request/url_request.h" 39#include "net/url_request/url_request_context.h" 40 41namespace net { 42 43namespace { 44 45// Protects |g_request_context|. 46pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER; 47URLRequestContext* g_request_context = NULL; 48 49// The default timeout for network fetches in NSS is 60 seconds. Choose a 50// saner upper limit for OCSP/CRL/AIA fetches. 51const int kNetworkFetchTimeoutInSecs = 15; 52 53class OCSPRequestSession; 54 55class OCSPIOLoop { 56 public: 57 void StartUsing() { 58 base::AutoLock autolock(lock_); 59 used_ = true; 60 io_loop_ = MessageLoopForIO::current(); 61 DCHECK(io_loop_); 62 } 63 64 // Called on IO loop. 65 void Shutdown(); 66 67 bool used() const { 68 base::AutoLock autolock(lock_); 69 return used_; 70 } 71 72 // Called from worker thread. 73 void PostTaskToIOLoop(const tracked_objects::Location& from_here, 74 const base::Closure& task); 75 76 void EnsureIOLoop(); 77 78 void AddRequest(OCSPRequestSession* request); 79 void RemoveRequest(OCSPRequestSession* request); 80 81 // Clears internal state and calls |StartUsing()|. Should be called only in 82 // the context of testing. 83 void ReuseForTesting() { 84 { 85 base::AutoLock autolock(lock_); 86 DCHECK(MessageLoopForIO::current()); 87 thread_checker_.DetachFromThread(); 88 thread_checker_.CalledOnValidThread(); 89 shutdown_ = false; 90 used_ = false; 91 } 92 StartUsing(); 93 } 94 95 private: 96 friend struct base::DefaultLazyInstanceTraits<OCSPIOLoop>; 97 98 OCSPIOLoop(); 99 ~OCSPIOLoop(); 100 101 void CancelAllRequests(); 102 103 mutable base::Lock lock_; 104 bool shutdown_; // Protected by |lock_|. 105 std::set<OCSPRequestSession*> requests_; // Protected by |lock_|. 106 bool used_; // Protected by |lock_|. 107 // This should not be modified after |used_|. 108 MessageLoopForIO* io_loop_; // Protected by |lock_|. 109 base::ThreadChecker thread_checker_; 110 111 DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop); 112}; 113 114base::LazyInstance<OCSPIOLoop>::Leaky 115 g_ocsp_io_loop = LAZY_INSTANCE_INITIALIZER; 116 117const int kRecvBufferSize = 4096; 118 119// All OCSP handlers should be called in the context of 120// CertVerifier's thread (i.e. worker pool, not on the I/O thread). 121// It supports blocking mode only. 122 123SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, 124 SEC_HTTP_SERVER_SESSION* pSession); 125SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session, 126 PRPollDesc **pPollDesc); 127SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session); 128 129SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session, 130 const char* http_protocol_variant, 131 const char* path_and_query_string, 132 const char* http_request_method, 133 const PRIntervalTime timeout, 134 SEC_HTTP_REQUEST_SESSION* pRequest); 135SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request, 136 const char* http_data, 137 const PRUint32 http_data_len, 138 const char* http_content_type); 139SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request, 140 const char* http_header_name, 141 const char* http_header_value); 142SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request, 143 PRPollDesc** pPollDesc, 144 PRUint16* http_response_code, 145 const char** http_response_content_type, 146 const char** http_response_headers, 147 const char** http_response_data, 148 PRUint32* http_response_data_len); 149SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request); 150 151char* GetAlternateOCSPAIAInfo(CERTCertificate *cert); 152 153class OCSPNSSInitialization { 154 private: 155 friend struct base::DefaultLazyInstanceTraits<OCSPNSSInitialization>; 156 157 OCSPNSSInitialization(); 158 ~OCSPNSSInitialization(); 159 160 SEC_HttpClientFcn client_fcn_; 161 162 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization); 163}; 164 165base::LazyInstance<OCSPNSSInitialization> g_ocsp_nss_initialization = 166 LAZY_INSTANCE_INITIALIZER; 167 168// Concrete class for SEC_HTTP_REQUEST_SESSION. 169// Public methods except virtual methods of URLRequest::Delegate 170// (On* methods) run on certificate verifier thread (worker thread). 171// Virtual methods of URLRequest::Delegate and private methods run 172// on IO thread. 173class OCSPRequestSession 174 : public base::RefCountedThreadSafe<OCSPRequestSession>, 175 public URLRequest::Delegate { 176 public: 177 OCSPRequestSession(const GURL& url, 178 const char* http_request_method, 179 base::TimeDelta timeout) 180 : url_(url), 181 http_request_method_(http_request_method), 182 timeout_(timeout), 183 request_(NULL), 184 buffer_(new IOBuffer(kRecvBufferSize)), 185 response_code_(-1), 186 cv_(&lock_), 187 io_loop_(NULL), 188 finished_(false) {} 189 190 void SetPostData(const char* http_data, PRUint32 http_data_len, 191 const char* http_content_type) { 192 upload_content_.assign(http_data, http_data_len); 193 upload_content_type_.assign(http_content_type); 194 } 195 196 void AddHeader(const char* http_header_name, const char* http_header_value) { 197 extra_request_headers_.SetHeader(http_header_name, 198 http_header_value); 199 } 200 201 void Start() { 202 // At this point, it runs on worker thread. 203 // |io_loop_| was initialized to be NULL in constructor, and 204 // set only in StartURLRequest, so no need to lock |lock_| here. 205 DCHECK(!io_loop_); 206 g_ocsp_io_loop.Get().PostTaskToIOLoop( 207 FROM_HERE, 208 base::Bind(&OCSPRequestSession::StartURLRequest, this)); 209 } 210 211 bool Started() const { 212 return request_ != NULL; 213 } 214 215 void Cancel() { 216 // IO thread may set |io_loop_| to NULL, so protect by |lock_|. 217 base::AutoLock autolock(lock_); 218 CancelLocked(); 219 } 220 221 bool Finished() const { 222 base::AutoLock autolock(lock_); 223 return finished_; 224 } 225 226 bool Wait() { 227 base::TimeDelta timeout = timeout_; 228 base::AutoLock autolock(lock_); 229 while (!finished_) { 230 base::TimeTicks last_time = base::TimeTicks::Now(); 231 cv_.TimedWait(timeout); 232 // Check elapsed time 233 base::TimeDelta elapsed_time = base::TimeTicks::Now() - last_time; 234 timeout -= elapsed_time; 235 if (timeout < base::TimeDelta()) { 236 VLOG(1) << "OCSP Timed out"; 237 if (!finished_) 238 CancelLocked(); 239 break; 240 } 241 } 242 return finished_; 243 } 244 245 const GURL& url() const { 246 return url_; 247 } 248 249 const std::string& http_request_method() const { 250 return http_request_method_; 251 } 252 253 base::TimeDelta timeout() const { 254 return timeout_; 255 } 256 257 PRUint16 http_response_code() const { 258 DCHECK(finished_); 259 return response_code_; 260 } 261 262 const std::string& http_response_content_type() const { 263 DCHECK(finished_); 264 return response_content_type_; 265 } 266 267 const std::string& http_response_headers() const { 268 DCHECK(finished_); 269 return response_headers_->raw_headers(); 270 } 271 272 const std::string& http_response_data() const { 273 DCHECK(finished_); 274 return data_; 275 } 276 277 virtual void OnReceivedRedirect(URLRequest* request, 278 const GURL& new_url, 279 bool* defer_redirect) OVERRIDE { 280 DCHECK_EQ(request, request_); 281 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 282 283 if (!new_url.SchemeIs("http")) { 284 // Prevent redirects to non-HTTP schemes, including HTTPS. This matches 285 // the initial check in OCSPServerSession::CreateRequest(). 286 CancelURLRequest(); 287 } 288 } 289 290 virtual void OnResponseStarted(URLRequest* request) OVERRIDE { 291 DCHECK_EQ(request, request_); 292 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 293 294 int bytes_read = 0; 295 if (request->status().is_success()) { 296 response_code_ = request_->GetResponseCode(); 297 response_headers_ = request_->response_headers(); 298 response_headers_->GetMimeType(&response_content_type_); 299 request_->Read(buffer_, kRecvBufferSize, &bytes_read); 300 } 301 OnReadCompleted(request_, bytes_read); 302 } 303 304 virtual void OnReadCompleted(URLRequest* request, 305 int bytes_read) OVERRIDE { 306 DCHECK_EQ(request, request_); 307 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 308 309 do { 310 if (!request_->status().is_success() || bytes_read <= 0) 311 break; 312 data_.append(buffer_->data(), bytes_read); 313 } while (request_->Read(buffer_, kRecvBufferSize, &bytes_read)); 314 315 if (!request_->status().is_io_pending()) { 316 delete request_; 317 request_ = NULL; 318 g_ocsp_io_loop.Get().RemoveRequest(this); 319 { 320 base::AutoLock autolock(lock_); 321 finished_ = true; 322 io_loop_ = NULL; 323 } 324 cv_.Signal(); 325 Release(); // Balanced with StartURLRequest(). 326 } 327 } 328 329 // Must be called on the IO loop thread. 330 void CancelURLRequest() { 331#ifndef NDEBUG 332 { 333 base::AutoLock autolock(lock_); 334 if (io_loop_) 335 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 336 } 337#endif 338 if (request_) { 339 request_->Cancel(); 340 delete request_; 341 request_ = NULL; 342 g_ocsp_io_loop.Get().RemoveRequest(this); 343 { 344 base::AutoLock autolock(lock_); 345 finished_ = true; 346 io_loop_ = NULL; 347 } 348 cv_.Signal(); 349 Release(); // Balanced with StartURLRequest(). 350 } 351 } 352 353 private: 354 friend class base::RefCountedThreadSafe<OCSPRequestSession>; 355 356 virtual ~OCSPRequestSession() { 357 // When this destructor is called, there should be only one thread that has 358 // a reference to this object, and so that thread doesn't need to lock 359 // |lock_| here. 360 DCHECK(!request_); 361 DCHECK(!io_loop_); 362 } 363 364 // Must call this method while holding |lock_|. 365 void CancelLocked() { 366 lock_.AssertAcquired(); 367 if (io_loop_) { 368 io_loop_->PostTask( 369 FROM_HERE, 370 base::Bind(&OCSPRequestSession::CancelURLRequest, this)); 371 } 372 } 373 374 // Runs on |g_ocsp_io_loop|'s IO loop. 375 void StartURLRequest() { 376 DCHECK(!request_); 377 378 pthread_mutex_lock(&g_request_context_lock); 379 URLRequestContext* url_request_context = g_request_context; 380 pthread_mutex_unlock(&g_request_context_lock); 381 382 if (url_request_context == NULL) 383 return; 384 385 { 386 base::AutoLock autolock(lock_); 387 DCHECK(!io_loop_); 388 io_loop_ = MessageLoopForIO::current(); 389 g_ocsp_io_loop.Get().AddRequest(this); 390 } 391 392 request_ = new URLRequest(url_, this, url_request_context); 393 // To meet the privacy requirements of incognito mode. 394 request_->set_load_flags(LOAD_DISABLE_CACHE | LOAD_DO_NOT_SAVE_COOKIES | 395 LOAD_DO_NOT_SEND_COOKIES); 396 397 if (http_request_method_ == "POST") { 398 DCHECK(!upload_content_.empty()); 399 DCHECK(!upload_content_type_.empty()); 400 401 request_->set_method("POST"); 402 extra_request_headers_.SetHeader( 403 HttpRequestHeaders::kContentType, upload_content_type_); 404 request_->AppendBytesToUpload(upload_content_.data(), 405 static_cast<int>(upload_content_.size())); 406 } 407 if (!extra_request_headers_.IsEmpty()) 408 request_->SetExtraRequestHeaders(extra_request_headers_); 409 410 request_->Start(); 411 AddRef(); // Release after |request_| deleted. 412 } 413 414 GURL url_; // The URL we eventually wound up at 415 std::string http_request_method_; 416 base::TimeDelta timeout_; // The timeout for OCSP 417 URLRequest* request_; // The actual request this wraps 418 scoped_refptr<IOBuffer> buffer_; // Read buffer 419 HttpRequestHeaders extra_request_headers_; 420 std::string upload_content_; // HTTP POST payload 421 std::string upload_content_type_; // MIME type of POST payload 422 423 int response_code_; // HTTP status code for the request 424 std::string response_content_type_; 425 scoped_refptr<HttpResponseHeaders> response_headers_; 426 std::string data_; // Results of the request 427 428 // |lock_| protects |finished_| and |io_loop_|. 429 mutable base::Lock lock_; 430 base::ConditionVariable cv_; 431 432 MessageLoop* io_loop_; // Message loop of the IO thread 433 bool finished_; 434 435 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession); 436}; 437 438// Concrete class for SEC_HTTP_SERVER_SESSION. 439class OCSPServerSession { 440 public: 441 OCSPServerSession(const char* host, PRUint16 port) 442 : host_and_port_(host, port) {} 443 ~OCSPServerSession() {} 444 445 OCSPRequestSession* CreateRequest(const char* http_protocol_variant, 446 const char* path_and_query_string, 447 const char* http_request_method, 448 const PRIntervalTime timeout) { 449 // We dont' support "https" because we haven't thought about 450 // whether it's safe to re-enter this code from talking to an OCSP 451 // responder over SSL. 452 if (strcmp(http_protocol_variant, "http") != 0) { 453 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); 454 return NULL; 455 } 456 457 std::string url_string(base::StringPrintf( 458 "%s://%s%s", 459 http_protocol_variant, 460 host_and_port_.ToString().c_str(), 461 path_and_query_string)); 462 VLOG(1) << "URL [" << url_string << "]"; 463 GURL url(url_string); 464 465 // NSS does not expose public functions to adjust the fetch timeout when 466 // using libpkix, so hardcode the upper limit for network fetches. 467 base::TimeDelta actual_timeout = std::min( 468 base::TimeDelta::FromSeconds(kNetworkFetchTimeoutInSecs), 469 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout))); 470 471 return new OCSPRequestSession(url, http_request_method, actual_timeout); 472 } 473 474 475 private: 476 HostPortPair host_and_port_; 477 478 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession); 479}; 480 481OCSPIOLoop::OCSPIOLoop() 482 : shutdown_(false), 483 used_(false), 484 io_loop_(NULL) { 485} 486 487OCSPIOLoop::~OCSPIOLoop() { 488 // IO thread was already deleted before the singleton is deleted 489 // in AtExitManager. 490 { 491 base::AutoLock autolock(lock_); 492 DCHECK(!io_loop_); 493 DCHECK(!used_); 494 DCHECK(shutdown_); 495 } 496 497 pthread_mutex_lock(&g_request_context_lock); 498 DCHECK(!g_request_context); 499 pthread_mutex_unlock(&g_request_context_lock); 500} 501 502void OCSPIOLoop::Shutdown() { 503 // Safe to read outside lock since we only write on IO thread anyway. 504 DCHECK(thread_checker_.CalledOnValidThread()); 505 506 // Prevent the worker thread from trying to access |io_loop_|. 507 { 508 base::AutoLock autolock(lock_); 509 io_loop_ = NULL; 510 used_ = false; 511 shutdown_ = true; 512 } 513 514 CancelAllRequests(); 515 516 pthread_mutex_lock(&g_request_context_lock); 517 g_request_context = NULL; 518 pthread_mutex_unlock(&g_request_context_lock); 519} 520 521void OCSPIOLoop::PostTaskToIOLoop( 522 const tracked_objects::Location& from_here, const base::Closure& task) { 523 base::AutoLock autolock(lock_); 524 if (io_loop_) 525 io_loop_->PostTask(from_here, task); 526} 527 528void OCSPIOLoop::EnsureIOLoop() { 529 base::AutoLock autolock(lock_); 530 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 531} 532 533void OCSPIOLoop::AddRequest(OCSPRequestSession* request) { 534 DCHECK(!ContainsKey(requests_, request)); 535 requests_.insert(request); 536} 537 538void OCSPIOLoop::RemoveRequest(OCSPRequestSession* request) { 539 DCHECK(ContainsKey(requests_, request)); 540 requests_.erase(request); 541} 542 543void OCSPIOLoop::CancelAllRequests() { 544 // CancelURLRequest() always removes the request from the requests_ 545 // set synchronously. 546 while (!requests_.empty()) 547 (*requests_.begin())->CancelURLRequest(); 548} 549 550OCSPNSSInitialization::OCSPNSSInitialization() { 551 // NSS calls the functions in the function table to download certificates 552 // or CRLs or talk to OCSP responders over HTTP. These functions must 553 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the 554 // residual error code from an earlier failed function call. 555 client_fcn_.version = 1; 556 SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1; 557 ft->createSessionFcn = OCSPCreateSession; 558 ft->keepAliveSessionFcn = OCSPKeepAliveSession; 559 ft->freeSessionFcn = OCSPFreeSession; 560 ft->createFcn = OCSPCreate; 561 ft->setPostDataFcn = OCSPSetPostData; 562 ft->addHeaderFcn = OCSPAddHeader; 563 ft->trySendAndReceiveFcn = OCSPTrySendAndReceive; 564 ft->cancelFcn = NULL; 565 ft->freeFcn = OCSPFree; 566 SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_); 567 if (status != SECSuccess) { 568 NOTREACHED() << "Error initializing OCSP: " << PR_GetError(); 569 } 570 571 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the 572 // CRLs for Network Solutions Certificate Authority have bad signatures, 573 // which causes certificates issued by that CA to be reported as revoked. 574 // By using OCSP for those certificates, which don't have AIA extensions, 575 // we can work around these bugs. See http://crbug.com/41730. 576 CERT_StringFromCertFcn old_callback = NULL; 577 status = CERT_RegisterAlternateOCSPAIAInfoCallBack( 578 GetAlternateOCSPAIAInfo, &old_callback); 579 if (status == SECSuccess) { 580 DCHECK(!old_callback); 581 } else { 582 NOTREACHED() << "Error initializing OCSP: " << PR_GetError(); 583 } 584} 585 586OCSPNSSInitialization::~OCSPNSSInitialization() { 587 SECStatus status = CERT_RegisterAlternateOCSPAIAInfoCallBack(NULL, NULL); 588 if (status != SECSuccess) { 589 LOG(ERROR) << "Error unregistering OCSP: " << PR_GetError(); 590 } 591} 592 593 594// OCSP Http Client functions. 595// Our Http Client functions operate in blocking mode. 596SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, 597 SEC_HTTP_SERVER_SESSION* pSession) { 598 VLOG(1) << "OCSP create session: host=" << host << " port=" << portnum; 599 pthread_mutex_lock(&g_request_context_lock); 600 URLRequestContext* request_context = g_request_context; 601 pthread_mutex_unlock(&g_request_context_lock); 602 if (request_context == NULL) { 603 LOG(ERROR) << "No URLRequestContext for NSS HTTP handler. host: " << host; 604 // The application failed to call SetURLRequestContextForNSSHttpIO or 605 // has already called ShutdownNSSHttpIO, so we can't create and use 606 // URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an accurate error 607 // code for these error conditions, but is close enough. 608 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); 609 return SECFailure; 610 } 611 *pSession = new OCSPServerSession(host, portnum); 612 return SECSuccess; 613} 614 615SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session, 616 PRPollDesc **pPollDesc) { 617 VLOG(1) << "OCSP keep alive"; 618 if (pPollDesc) 619 *pPollDesc = NULL; 620 return SECSuccess; 621} 622 623SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) { 624 VLOG(1) << "OCSP free session"; 625 delete reinterpret_cast<OCSPServerSession*>(session); 626 return SECSuccess; 627} 628 629SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session, 630 const char* http_protocol_variant, 631 const char* path_and_query_string, 632 const char* http_request_method, 633 const PRIntervalTime timeout, 634 SEC_HTTP_REQUEST_SESSION* pRequest) { 635 VLOG(1) << "OCSP create protocol=" << http_protocol_variant 636 << " path_and_query=" << path_and_query_string 637 << " http_request_method=" << http_request_method 638 << " timeout=" << timeout; 639 OCSPServerSession* ocsp_session = 640 reinterpret_cast<OCSPServerSession*>(session); 641 642 OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant, 643 path_and_query_string, 644 http_request_method, 645 timeout); 646 SECStatus rv = SECFailure; 647 if (req) { 648 req->AddRef(); // Release in OCSPFree(). 649 rv = SECSuccess; 650 } 651 *pRequest = req; 652 return rv; 653} 654 655SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request, 656 const char* http_data, 657 const PRUint32 http_data_len, 658 const char* http_content_type) { 659 VLOG(1) << "OCSP set post data len=" << http_data_len; 660 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request); 661 662 req->SetPostData(http_data, http_data_len, http_content_type); 663 return SECSuccess; 664} 665 666SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request, 667 const char* http_header_name, 668 const char* http_header_value) { 669 VLOG(1) << "OCSP add header name=" << http_header_name 670 << " value=" << http_header_value; 671 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request); 672 673 req->AddHeader(http_header_name, http_header_value); 674 return SECSuccess; 675} 676 677// Sets response of |req| in the output parameters. 678// It is helper routine for OCSP trySendAndReceiveFcn. 679// |http_response_data_len| could be used as input parameter. If it has 680// non-zero value, it is considered as maximum size of |http_response_data|. 681SECStatus OCSPSetResponse(OCSPRequestSession* req, 682 PRUint16* http_response_code, 683 const char** http_response_content_type, 684 const char** http_response_headers, 685 const char** http_response_data, 686 PRUint32* http_response_data_len) { 687 DCHECK(req->Finished()); 688 const std::string& data = req->http_response_data(); 689 if (http_response_data_len && *http_response_data_len) { 690 if (*http_response_data_len < data.size()) { 691 LOG(ERROR) << "response body too large: " << *http_response_data_len 692 << " < " << data.size(); 693 *http_response_data_len = data.size(); 694 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); 695 return SECFailure; 696 } 697 } 698 VLOG(1) << "OCSP response " 699 << " response_code=" << req->http_response_code() 700 << " content_type=" << req->http_response_content_type() 701 << " header=" << req->http_response_headers() 702 << " data_len=" << data.size(); 703 if (http_response_code) 704 *http_response_code = req->http_response_code(); 705 if (http_response_content_type) 706 *http_response_content_type = req->http_response_content_type().c_str(); 707 if (http_response_headers) 708 *http_response_headers = req->http_response_headers().c_str(); 709 if (http_response_data) 710 *http_response_data = data.data(); 711 if (http_response_data_len) 712 *http_response_data_len = data.size(); 713 return SECSuccess; 714} 715 716SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request, 717 PRPollDesc** pPollDesc, 718 PRUint16* http_response_code, 719 const char** http_response_content_type, 720 const char** http_response_headers, 721 const char** http_response_data, 722 PRUint32* http_response_data_len) { 723 if (http_response_data_len) { 724 // We must always set an output value, even on failure. The output value 0 725 // means the failure was unrelated to the acceptable response data length. 726 *http_response_data_len = 0; 727 } 728 729 VLOG(1) << "OCSP try send and receive"; 730 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request); 731 // We support blocking mode only. 732 if (pPollDesc) 733 *pPollDesc = NULL; 734 735 if (req->Started() || req->Finished()) { 736 // We support blocking mode only, so this function shouldn't be called 737 // again when req has stareted or finished. 738 NOTREACHED(); 739 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation. 740 return SECFailure; 741 } 742 743 const base::Time start_time = base::Time::Now(); 744 bool request_ok = true; 745 req->Start(); 746 if (!req->Wait() || req->http_response_code() == static_cast<PRUint16>(-1)) { 747 // If the response code is -1, the request failed and there is no response. 748 request_ok = false; 749 } 750 const base::TimeDelta duration = base::Time::Now() - start_time; 751 752 // For metrics, we want to know if the request was 'successful' or not. 753 // |request_ok| determines if we'll pass the response back to NSS and |ok| 754 // keep track of if we think the response was good. 755 bool ok = true; 756 if (!request_ok || 757 (req->http_response_code() >= 400 && req->http_response_code() < 600) || 758 req->http_response_data().size() == 0 || 759 // 0x30 is the ASN.1 DER encoding of a SEQUENCE. All valid OCSP/CRL/CRT 760 // responses must start with this. If we didn't check for this then a 761 // captive portal could provide an HTML reply that we would count as a 762 // 'success' (although it wouldn't count in NSS, of course). 763 req->http_response_data().data()[0] != 0x30) { 764 ok = false; 765 } 766 767 // We want to know if this was: 768 // 1) An OCSP request 769 // 2) A CRL request 770 // 3) A request for a missing intermediate certificate 771 // There's no sure way to do this, so we use heuristics like MIME type and 772 // URL. 773 const char* mime_type = ""; 774 if (ok) 775 mime_type = req->http_response_content_type().c_str(); 776 bool is_ocsp = 777 strcasecmp(mime_type, "application/ocsp-response") == 0; 778 bool is_crl = strcasecmp(mime_type, "application/x-pkcs7-crl") == 0 || 779 strcasecmp(mime_type, "application/x-x509-crl") == 0 || 780 strcasecmp(mime_type, "application/pkix-crl") == 0; 781 bool is_cert = 782 strcasecmp(mime_type, "application/x-x509-ca-cert") == 0 || 783 strcasecmp(mime_type, "application/x-x509-server-cert") == 0 || 784 strcasecmp(mime_type, "application/pkix-cert") == 0 || 785 strcasecmp(mime_type, "application/pkcs7-mime") == 0; 786 787 if (!is_cert && !is_crl && !is_ocsp) { 788 // We didn't get a hint from the MIME type, so do the best that we can. 789 const std::string path = req->url().path(); 790 const std::string host = req->url().host(); 791 is_crl = strcasestr(path.c_str(), ".crl") != NULL; 792 is_cert = strcasestr(path.c_str(), ".crt") != NULL || 793 strcasestr(path.c_str(), ".p7c") != NULL || 794 strcasestr(path.c_str(), ".cer") != NULL; 795 is_ocsp = strcasestr(host.c_str(), "ocsp") != NULL || 796 req->http_request_method() == "POST"; 797 } 798 799 if (is_ocsp) { 800 if (ok) { 801 UMA_HISTOGRAM_TIMES("Net.OCSPRequestTimeMs", duration); 802 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", true); 803 } else { 804 UMA_HISTOGRAM_TIMES("Net.OCSPRequestFailedTimeMs", duration); 805 UMA_HISTOGRAM_BOOLEAN("Net.OCSPRequestSuccess", false); 806 } 807 } else if (is_crl) { 808 if (ok) { 809 UMA_HISTOGRAM_TIMES("Net.CRLRequestTimeMs", duration); 810 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", true); 811 } else { 812 UMA_HISTOGRAM_TIMES("Net.CRLRequestFailedTimeMs", duration); 813 UMA_HISTOGRAM_BOOLEAN("Net.CRLRequestSuccess", false); 814 } 815 } else if (is_cert) { 816 if (ok) 817 UMA_HISTOGRAM_TIMES("Net.CRTRequestTimeMs", duration); 818 } else { 819 if (ok) 820 UMA_HISTOGRAM_TIMES("Net.UnknownTypeRequestTimeMs", duration); 821 } 822 823 if (!request_ok) { 824 PORT_SetError(SEC_ERROR_BAD_HTTP_RESPONSE); // Simple approximation. 825 return SECFailure; 826 } 827 828 return OCSPSetResponse( 829 req, http_response_code, 830 http_response_content_type, 831 http_response_headers, 832 http_response_data, 833 http_response_data_len); 834} 835 836SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) { 837 VLOG(1) << "OCSP free"; 838 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request); 839 req->Cancel(); 840 req->Release(); 841 return SECSuccess; 842} 843 844// Data for GetAlternateOCSPAIAInfo. 845 846// CN=Network Solutions Certificate Authority,O=Network Solutions L.L.C.,C=US 847// 848// There are two CAs with this name. Their key IDs are listed next. 849const unsigned char network_solutions_ca_name[] = { 850 0x30, 0x62, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 851 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x21, 0x30, 0x1f, 0x06, 852 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x4e, 0x65, 0x74, 0x77, 853 0x6f, 0x72, 0x6b, 0x20, 0x53, 0x6f, 0x6c, 0x75, 0x74, 0x69, 854 0x6f, 0x6e, 0x73, 0x20, 0x4c, 0x2e, 0x4c, 0x2e, 0x43, 0x2e, 855 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 856 0x27, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x53, 857 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x43, 858 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 859 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79 860}; 861const unsigned int network_solutions_ca_name_len = 100; 862 863// This CA is an intermediate CA, subordinate to UTN-USERFirst-Hardware. 864const unsigned char network_solutions_ca_key_id[] = { 865 0x3c, 0x41, 0xe2, 0x8f, 0x08, 0x08, 0xa9, 0x4c, 0x25, 0x89, 866 0x8d, 0x6d, 0xc5, 0x38, 0xd0, 0xfc, 0x85, 0x8c, 0x62, 0x17 867}; 868const unsigned int network_solutions_ca_key_id_len = 20; 869 870// This CA is a root CA. It is also cross-certified by 871// UTN-USERFirst-Hardware. 872const unsigned char network_solutions_ca_key_id2[] = { 873 0x21, 0x30, 0xc9, 0xfb, 0x00, 0xd7, 0x4e, 0x98, 0xda, 0x87, 874 0xaa, 0x2a, 0xd0, 0xa7, 0x2e, 0xb1, 0x40, 0x31, 0xa7, 0x4c 875}; 876const unsigned int network_solutions_ca_key_id2_len = 20; 877 878// An entry in our OCSP responder table. |issuer| and |issuer_key_id| are 879// the key. |ocsp_url| is the value. 880struct OCSPResponderTableEntry { 881 SECItem issuer; 882 SECItem issuer_key_id; 883 const char *ocsp_url; 884}; 885 886const OCSPResponderTableEntry g_ocsp_responder_table[] = { 887 { 888 { 889 siBuffer, 890 const_cast<unsigned char*>(network_solutions_ca_name), 891 network_solutions_ca_name_len 892 }, 893 { 894 siBuffer, 895 const_cast<unsigned char*>(network_solutions_ca_key_id), 896 network_solutions_ca_key_id_len 897 }, 898 "http://ocsp.netsolssl.com" 899 }, 900 { 901 { 902 siBuffer, 903 const_cast<unsigned char*>(network_solutions_ca_name), 904 network_solutions_ca_name_len 905 }, 906 { 907 siBuffer, 908 const_cast<unsigned char*>(network_solutions_ca_key_id2), 909 network_solutions_ca_key_id2_len 910 }, 911 "http://ocsp.netsolssl.com" 912 } 913}; 914 915char* GetAlternateOCSPAIAInfo(CERTCertificate *cert) { 916 if (cert && !cert->isRoot && cert->authKeyID) { 917 for (unsigned int i=0; i < arraysize(g_ocsp_responder_table); i++) { 918 if (SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer, 919 &cert->derIssuer) == SECEqual && 920 SECITEM_CompareItem(&g_ocsp_responder_table[i].issuer_key_id, 921 &cert->authKeyID->keyID) == SECEqual) { 922 return PORT_Strdup(g_ocsp_responder_table[i].ocsp_url); 923 } 924 } 925 } 926 927 return NULL; 928} 929 930} // anonymous namespace 931 932void SetMessageLoopForNSSHttpIO() { 933 // Must have a MessageLoopForIO. 934 DCHECK(MessageLoopForIO::current()); 935 936 bool used = g_ocsp_io_loop.Get().used(); 937 938 // Should not be called when g_ocsp_io_loop has already been used. 939 DCHECK(!used); 940} 941 942void EnsureNSSHttpIOInit() { 943 g_ocsp_io_loop.Get().StartUsing(); 944 g_ocsp_nss_initialization.Get(); 945} 946 947void ShutdownNSSHttpIO() { 948 g_ocsp_io_loop.Get().Shutdown(); 949} 950 951void ResetNSSHttpIOForTesting() { 952 g_ocsp_io_loop.Get().ReuseForTesting(); 953} 954 955// This function would be called before NSS initialization. 956void SetURLRequestContextForNSSHttpIO(URLRequestContext* request_context) { 957 pthread_mutex_lock(&g_request_context_lock); 958 if (request_context) { 959 DCHECK(!g_request_context); 960 } 961 g_request_context = request_context; 962 pthread_mutex_unlock(&g_request_context_lock); 963} 964 965} // namespace net 966