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