1 // Copyright (c) 2009 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 <secerr.h> 13 14 #include <string> 15 16 #include "base/compiler_specific.h" 17 #include "base/condition_variable.h" 18 #include "base/logging.h" 19 #include "base/message_loop.h" 20 #include "base/singleton.h" 21 #include "base/thread.h" 22 #include "base/time.h" 23 #include "googleurl/src/gurl.h" 24 #include "net/base/io_buffer.h" 25 #include "net/base/load_flags.h" 26 #include "net/http/http_response_headers.h" 27 #include "net/url_request/url_request.h" 28 #include "net/url_request/url_request_context.h" 29 30 namespace { 31 32 const int kRecvBufferSize = 4096; 33 34 // All OCSP handlers should be called in the context of 35 // CertVerifier's thread (i.e. worker pool, not on the I/O thread). 36 // It supports blocking mode only. 37 38 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, 39 SEC_HTTP_SERVER_SESSION* pSession); 40 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session, 41 PRPollDesc **pPollDesc); 42 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session); 43 44 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session, 45 const char* http_protocol_variant, 46 const char* path_and_query_string, 47 const char* http_request_method, 48 const PRIntervalTime timeout, 49 SEC_HTTP_REQUEST_SESSION* pRequest); 50 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request, 51 const char* http_data, 52 const PRUint32 http_data_len, 53 const char* http_content_type); 54 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request, 55 const char* http_header_name, 56 const char* http_header_value); 57 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request, 58 PRPollDesc** pPollDesc, 59 PRUint16* http_response_code, 60 const char** http_response_content_type, 61 const char** http_response_headers, 62 const char** http_response_data, 63 PRUint32* http_response_data_len); 64 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request); 65 66 class OCSPInitSingleton : public MessageLoop::DestructionObserver { 67 public: 68 // Called on IO thread. 69 virtual void WillDestroyCurrentMessageLoop() { 70 AutoLock autolock(lock_); 71 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 72 io_loop_ = NULL; 73 request_context_ = NULL; 74 }; 75 76 // Called from worker thread. 77 void PostTaskToIOLoop( 78 const tracked_objects::Location& from_here, Task* task) { 79 AutoLock autolock(lock_); 80 if (io_loop_) 81 io_loop_->PostTask(from_here, task); 82 } 83 84 // This is static method because it is called before NSS initialization, 85 // that is, before OCSPInitSingleton is initialized. 86 static void set_url_request_context(URLRequestContext* request_context) { 87 request_context_ = request_context; 88 } 89 static URLRequestContext* url_request_context() { 90 return request_context_; 91 } 92 93 private: 94 friend struct DefaultSingletonTraits<OCSPInitSingleton>; 95 96 OCSPInitSingleton() 97 : io_loop_(MessageLoopForIO::current()) { 98 DCHECK(io_loop_); 99 io_loop_->AddDestructionObserver(this); 100 client_fcn_.version = 1; 101 SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1; 102 ft->createSessionFcn = OCSPCreateSession; 103 ft->keepAliveSessionFcn = OCSPKeepAliveSession; 104 ft->freeSessionFcn = OCSPFreeSession; 105 ft->createFcn = OCSPCreate; 106 ft->setPostDataFcn = OCSPSetPostData; 107 ft->addHeaderFcn = OCSPAddHeader; 108 ft->trySendAndReceiveFcn = OCSPTrySendAndReceive; 109 ft->cancelFcn = NULL; 110 ft->freeFcn = OCSPFree; 111 SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_); 112 if (status != SECSuccess) { 113 NOTREACHED() << "Error initializing OCSP: " << PR_GetError(); 114 } 115 } 116 117 virtual ~OCSPInitSingleton() { 118 // IO thread was already deleted before the singleton is deleted 119 // in AtExitManager. 120 AutoLock autolock(lock_); 121 DCHECK(!io_loop_); 122 DCHECK(!request_context_); 123 } 124 125 SEC_HttpClientFcn client_fcn_; 126 127 // |lock_| protects |io_loop_|. 128 Lock lock_; 129 // I/O thread. 130 MessageLoop* io_loop_; // I/O thread 131 // URLRequestContext for OCSP handlers. 132 static URLRequestContext* request_context_; 133 134 DISALLOW_COPY_AND_ASSIGN(OCSPInitSingleton); 135 }; 136 137 URLRequestContext* OCSPInitSingleton::request_context_ = NULL; 138 139 // Concrete class for SEC_HTTP_REQUEST_SESSION. 140 // Public methods except virtual methods of URLRequest::Delegate (On* methods) 141 // run on certificate verifier thread (worker thread). 142 // Virtual methods of URLRequest::Delegate and private methods run 143 // on IO thread. 144 class OCSPRequestSession 145 : public base::RefCountedThreadSafe<OCSPRequestSession>, 146 public URLRequest::Delegate, 147 public MessageLoop::DestructionObserver { 148 public: 149 OCSPRequestSession(const GURL& url, 150 const char* http_request_method, 151 base::TimeDelta timeout) 152 : url_(url), 153 http_request_method_(http_request_method), 154 timeout_(timeout), 155 request_(NULL), 156 buffer_(new net::IOBuffer(kRecvBufferSize)), 157 response_code_(-1), 158 cv_(&lock_), 159 io_loop_(NULL), 160 finished_(false) {} 161 162 void SetPostData(const char* http_data, PRUint32 http_data_len, 163 const char* http_content_type) { 164 upload_content_.assign(http_data, http_data_len); 165 upload_content_type_.assign(http_content_type); 166 } 167 168 void AddHeader(const char* http_header_name, const char* http_header_value) { 169 if (!extra_request_headers_.empty()) 170 extra_request_headers_ += "\r\n"; 171 StringAppendF(&extra_request_headers_, 172 "%s: %s", http_header_name, 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 Singleton<OCSPInitSingleton>()->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 AutoLock autolock(lock_); 192 CancelLocked(); 193 } 194 195 bool Finished() const { 196 AutoLock autolock(lock_); 197 return finished_; 198 } 199 200 bool Wait() { 201 base::TimeDelta timeout = timeout_; 202 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 LOG(INFO) << "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 OnResponseStarted(URLRequest* request) { 252 DCHECK_EQ(request, request_); 253 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 254 255 int bytes_read = 0; 256 if (request->status().is_success()) { 257 response_code_ = request_->GetResponseCode(); 258 response_headers_ = request_->response_headers(); 259 response_headers_->GetMimeType(&response_content_type_); 260 request_->Read(buffer_, kRecvBufferSize, &bytes_read); 261 } 262 OnReadCompleted(request_, bytes_read); 263 } 264 265 virtual void OnReadCompleted(URLRequest* request, int bytes_read) { 266 DCHECK_EQ(request, request_); 267 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 268 269 do { 270 if (!request_->status().is_success() || bytes_read <= 0) 271 break; 272 data_.append(buffer_->data(), bytes_read); 273 } while (request_->Read(buffer_, kRecvBufferSize, &bytes_read)); 274 275 if (!request_->status().is_io_pending()) { 276 delete request_; 277 request_ = NULL; 278 io_loop_->RemoveDestructionObserver(this); 279 { 280 AutoLock autolock(lock_); 281 finished_ = true; 282 io_loop_ = NULL; 283 } 284 cv_.Signal(); 285 Release(); // Balanced with StartURLRequest(). 286 } 287 } 288 289 virtual void WillDestroyCurrentMessageLoop() { 290 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 291 { 292 AutoLock autolock(lock_); 293 io_loop_ = NULL; 294 } 295 CancelURLRequest(); 296 } 297 298 private: 299 friend class base::RefCountedThreadSafe<OCSPRequestSession>; 300 301 virtual ~OCSPRequestSession() { 302 // When this destructor is called, there should be only one thread that has 303 // a reference to this object, and so that thread doesn't need to lock 304 // |lock_| here. 305 DCHECK(!request_); 306 DCHECK(!io_loop_); 307 } 308 309 // Must call this method while holding |lock_|. 310 void CancelLocked() { 311 lock_.AssertAcquired(); 312 if (io_loop_) { 313 io_loop_->PostTask( 314 FROM_HERE, 315 NewRunnableMethod(this, &OCSPRequestSession::CancelURLRequest)); 316 } 317 } 318 319 void StartURLRequest() { 320 DCHECK(!request_); 321 322 URLRequestContext* url_request_context = 323 OCSPInitSingleton::url_request_context(); 324 if (url_request_context == NULL) 325 return; 326 327 { 328 AutoLock autolock(lock_); 329 DCHECK(!io_loop_); 330 io_loop_ = MessageLoopForIO::current(); 331 io_loop_->AddDestructionObserver(this); 332 } 333 334 request_ = new URLRequest(url_, this); 335 request_->set_context(url_request_context); 336 // To meet the privacy requirements of off-the-record mode. 337 request_->set_load_flags( 338 net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SAVE_COOKIES | 339 net::LOAD_DO_NOT_SEND_COOKIES); 340 341 if (http_request_method_ == "POST") { 342 DCHECK(!upload_content_.empty()); 343 DCHECK(!upload_content_type_.empty()); 344 345 request_->set_method("POST"); 346 if (!extra_request_headers_.empty()) 347 extra_request_headers_ += "\r\n"; 348 StringAppendF(&extra_request_headers_, 349 "Content-Type: %s", upload_content_type_.c_str()); 350 request_->AppendBytesToUpload(upload_content_.data(), 351 static_cast<int>(upload_content_.size())); 352 } 353 if (!extra_request_headers_.empty()) 354 request_->SetExtraRequestHeaders(extra_request_headers_); 355 356 request_->Start(); 357 AddRef(); // Release after |request_| deleted. 358 } 359 360 void CancelURLRequest() { 361 #ifndef NDEBUG 362 { 363 AutoLock autolock(lock_); 364 if (io_loop_) 365 DCHECK_EQ(MessageLoopForIO::current(), io_loop_); 366 } 367 #endif 368 if (request_) { 369 request_->Cancel(); 370 delete request_; 371 request_ = NULL; 372 // |io_loop_| may be NULL here if it called from 373 // WillDestroyCurrentMessageLoop(). 374 if (io_loop_) 375 io_loop_->RemoveDestructionObserver(this); 376 { 377 AutoLock autolock(lock_); 378 finished_ = true; 379 io_loop_ = NULL; 380 } 381 cv_.Signal(); 382 Release(); // Balanced with StartURLRequest(). 383 } 384 } 385 386 GURL url_; // The URL we eventually wound up at 387 std::string http_request_method_; 388 base::TimeDelta timeout_; // The timeout for OCSP 389 URLRequest* request_; // The actual request this wraps 390 scoped_refptr<net::IOBuffer> buffer_; // Read buffer 391 std::string extra_request_headers_; // Extra headers for the request, if any 392 std::string upload_content_; // HTTP POST payload 393 std::string upload_content_type_; // MIME type of POST payload 394 395 int response_code_; // HTTP status code for the request 396 std::string response_content_type_; 397 scoped_refptr<net::HttpResponseHeaders> response_headers_; 398 std::string data_; // Results of the requst 399 400 // |lock_| protects |finished_| and |io_loop_|. 401 mutable Lock lock_; 402 ConditionVariable cv_; 403 404 MessageLoop* io_loop_; // Message loop of the IO thread 405 bool finished_; 406 407 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession); 408 }; 409 410 // Concrete class for SEC_HTTP_SERVER_SESSION. 411 class OCSPServerSession { 412 public: 413 OCSPServerSession(const char* host, PRUint16 port) 414 : host_(host), port_(port) {} 415 ~OCSPServerSession() {} 416 417 OCSPRequestSession* CreateRequest(const char* http_protocol_variant, 418 const char* path_and_query_string, 419 const char* http_request_method, 420 const PRIntervalTime timeout) { 421 // We dont' support "https" because we haven't thought about 422 // whether it's safe to re-enter this code from talking to an OCSP 423 // responder over SSL. 424 if (strcmp(http_protocol_variant, "http") != 0) 425 return NULL; 426 427 // TODO(ukai): If |host| is an IPv6 literal, we need to quote it with 428 // square brackets []. 429 std::string url_string(StringPrintf("%s://%s:%d%s", 430 http_protocol_variant, 431 host_.c_str(), 432 port_, 433 path_and_query_string)); 434 LOG(INFO) << "URL [" << url_string << "]"; 435 GURL url(url_string); 436 return new OCSPRequestSession( 437 url, http_request_method, 438 base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout))); 439 } 440 441 442 private: 443 std::string host_; 444 int port_; 445 446 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession); 447 }; 448 449 450 // OCSP Http Client functions. 451 // Our Http Client functions operate in blocking mode. 452 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, 453 SEC_HTTP_SERVER_SESSION* pSession) { 454 LOG(INFO) << "OCSP create session: host=" << host << " port=" << portnum; 455 DCHECK(!MessageLoop::current()); 456 if (OCSPInitSingleton::url_request_context() == NULL) { 457 LOG(ERROR) << "No URLRequestContext for OCSP handler."; 458 return SECFailure; 459 } 460 *pSession = new OCSPServerSession(host, portnum); 461 return SECSuccess; 462 } 463 464 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session, 465 PRPollDesc **pPollDesc) { 466 LOG(INFO) << "OCSP keep alive"; 467 DCHECK(!MessageLoop::current()); 468 if (pPollDesc) 469 *pPollDesc = NULL; 470 return SECSuccess; 471 } 472 473 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) { 474 LOG(INFO) << "OCSP free session"; 475 DCHECK(!MessageLoop::current()); 476 delete reinterpret_cast<OCSPServerSession*>(session); 477 return SECSuccess; 478 } 479 480 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session, 481 const char* http_protocol_variant, 482 const char* path_and_query_string, 483 const char* http_request_method, 484 const PRIntervalTime timeout, 485 SEC_HTTP_REQUEST_SESSION* pRequest) { 486 LOG(INFO) << "OCSP create protocol=" << http_protocol_variant 487 << " path_and_query=" << path_and_query_string 488 << " http_request_method=" << http_request_method 489 << " timeout=" << timeout; 490 DCHECK(!MessageLoop::current()); 491 OCSPServerSession* ocsp_session = 492 reinterpret_cast<OCSPServerSession*>(session); 493 494 OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant, 495 path_and_query_string, 496 http_request_method, 497 timeout); 498 SECStatus rv = SECFailure; 499 if (req) { 500 req->AddRef(); // Release in OCSPFree(). 501 rv = SECSuccess; 502 } 503 *pRequest = req; 504 return rv; 505 } 506 507 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request, 508 const char* http_data, 509 const PRUint32 http_data_len, 510 const char* http_content_type) { 511 LOG(INFO) << "OCSP set post data len=" << http_data_len; 512 DCHECK(!MessageLoop::current()); 513 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request); 514 515 req->SetPostData(http_data, http_data_len, http_content_type); 516 return SECSuccess; 517 } 518 519 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request, 520 const char* http_header_name, 521 const char* http_header_value) { 522 LOG(INFO) << "OCSP add header name=" << http_header_name 523 << " value=" << http_header_value; 524 DCHECK(!MessageLoop::current()); 525 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request); 526 527 req->AddHeader(http_header_name, http_header_value); 528 return SECSuccess; 529 } 530 531 // Sets response of |req| in the output parameters. 532 // It is helper routine for OCSP trySendAndReceiveFcn. 533 // |http_response_data_len| could be used as input parameter. If it has 534 // non-zero value, it is considered as maximum size of |http_response_data|. 535 bool OCSPSetResponse(OCSPRequestSession* req, 536 PRUint16* http_response_code, 537 const char** http_response_content_type, 538 const char** http_response_headers, 539 const char** http_response_data, 540 PRUint32* http_response_data_len) { 541 DCHECK(req->Finished()); 542 const std::string& data = req->http_response_data(); 543 if (http_response_data_len && *http_response_data_len) { 544 if (*http_response_data_len < data.size()) { 545 LOG(ERROR) << "data size too large: " << *http_response_data_len 546 << " < " << data.size(); 547 *http_response_data_len = data.size(); 548 return false; 549 } 550 } 551 LOG(INFO) << "OCSP response " 552 << " response_code=" << req->http_response_code() 553 << " content_type=" << req->http_response_content_type() 554 << " header=" << req->http_response_headers() 555 << " data_len=" << data.size(); 556 if (http_response_code) 557 *http_response_code = req->http_response_code(); 558 if (http_response_content_type) 559 *http_response_content_type = req->http_response_content_type().c_str(); 560 if (http_response_headers) 561 *http_response_headers = req->http_response_headers().c_str(); 562 if (http_response_data) 563 *http_response_data = data.data(); 564 if (http_response_data_len) 565 *http_response_data_len = data.size(); 566 return true; 567 } 568 569 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request, 570 PRPollDesc** pPollDesc, 571 PRUint16* http_response_code, 572 const char** http_response_content_type, 573 const char** http_response_headers, 574 const char** http_response_data, 575 PRUint32* http_response_data_len) { 576 LOG(INFO) << "OCSP try send and receive"; 577 DCHECK(!MessageLoop::current()); 578 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request); 579 // We support blocking mode only. 580 if (pPollDesc) 581 *pPollDesc = NULL; 582 583 if (req->Started() || req->Finished()) { 584 // We support blocking mode only, so this function shouldn't be called 585 // again when req has stareted or finished. 586 NOTREACHED(); 587 goto failed; 588 } 589 req->Start(); 590 if (!req->Wait()) 591 goto failed; 592 593 // If the response code is -1, the request failed and there is no response. 594 if (req->http_response_code() == static_cast<PRUint16>(-1)) 595 goto failed; 596 597 return OCSPSetResponse( 598 req, http_response_code, 599 http_response_content_type, 600 http_response_headers, 601 http_response_data, 602 http_response_data_len) ? SECSuccess : SECFailure; 603 604 failed: 605 if (http_response_data_len) { 606 // We must always set an output value, even on failure. The output value 0 607 // means the failure was unrelated to the acceptable response data length. 608 *http_response_data_len = 0; 609 } 610 return SECFailure; 611 } 612 613 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) { 614 LOG(INFO) << "OCSP free"; 615 DCHECK(!MessageLoop::current()); 616 OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request); 617 req->Cancel(); 618 req->Release(); 619 return SECSuccess; 620 } 621 622 } // anonymous namespace 623 624 namespace net { 625 626 void EnsureOCSPInit() { 627 Singleton<OCSPInitSingleton>::get(); 628 } 629 630 // This function would be called before NSS initialization. 631 void SetURLRequestContextForOCSP(URLRequestContext* request_context) { 632 OCSPInitSingleton::set_url_request_context(request_context); 633 } 634 635 URLRequestContext* GetURLRequestContextForOCSP() { 636 return OCSPInitSingleton::url_request_context(); 637 } 638 639 } // namespace net 640