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.
5 #include "net/ocsp/nss_ocsp.h"
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"
32 const int kRecvBufferSize
= 4096;
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.
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
);
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
);
66 class OCSPInitSingleton
: public MessageLoop::DestructionObserver
{
68 // Called on IO thread.
69 virtual void WillDestroyCurrentMessageLoop() {
70 AutoLock
autolock(lock_
);
71 DCHECK_EQ(MessageLoopForIO::current(), io_loop_
);
73 request_context_
= NULL
;
76 // Called from worker thread.
77 void PostTaskToIOLoop(
78 const tracked_objects::Location
& from_here
, Task
* task
) {
79 AutoLock
autolock(lock_
);
81 io_loop_
->PostTask(from_here
, task
);
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
;
89 static URLRequestContext
* url_request_context() {
90 return request_context_
;
94 friend struct DefaultSingletonTraits
<OCSPInitSingleton
>;
97 : io_loop_(MessageLoopForIO::current()) {
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();
117 virtual ~OCSPInitSingleton() {
118 // IO thread was already deleted before the singleton is deleted
120 AutoLock
autolock(lock_
);
122 DCHECK(!request_context_
);
125 SEC_HttpClientFcn client_fcn_
;
127 // |lock_| protects |io_loop_|.
130 MessageLoop
* io_loop_
; // I/O thread
131 // URLRequestContext for OCSP handlers.
132 static URLRequestContext
* request_context_
;
134 DISALLOW_COPY_AND_ASSIGN(OCSPInitSingleton
);
137 URLRequestContext
* OCSPInitSingleton::request_context_
= NULL
;
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
144 class OCSPRequestSession
145 : public base::RefCountedThreadSafe
<OCSPRequestSession
>,
146 public URLRequest::Delegate
,
147 public MessageLoop::DestructionObserver
{
149 OCSPRequestSession(const GURL
& url
,
150 const char* http_request_method
,
151 base::TimeDelta timeout
)
153 http_request_method_(http_request_method
),
156 buffer_(new net::IOBuffer(kRecvBufferSize
)),
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
);
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
);
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.
180 Singleton
<OCSPInitSingleton
>()->PostTaskToIOLoop(
182 NewRunnableMethod(this, &OCSPRequestSession::StartURLRequest
));
185 bool Started() const {
186 return request_
!= NULL
;
190 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
191 AutoLock
autolock(lock_
);
195 bool Finished() const {
196 AutoLock
autolock(lock_
);
201 base::TimeDelta timeout
= timeout_
;
202 AutoLock
autolock(lock_
);
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";
219 const GURL
& url() const {
223 const std::string
& http_request_method() const {
224 return http_request_method_
;
227 base::TimeDelta
timeout() const {
231 PRUint16
http_response_code() const {
233 return response_code_
;
236 const std::string
& http_response_content_type() const {
238 return response_content_type_
;
241 const std::string
& http_response_headers() const {
243 return response_headers_
->raw_headers();
246 const std::string
& http_response_data() const {
251 virtual void OnResponseStarted(URLRequest
* request
) {
252 DCHECK_EQ(request
, request_
);
253 DCHECK_EQ(MessageLoopForIO::current(), io_loop_
);
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
);
262 OnReadCompleted(request_
, bytes_read
);
265 virtual void OnReadCompleted(URLRequest
* request
, int bytes_read
) {
266 DCHECK_EQ(request
, request_
);
267 DCHECK_EQ(MessageLoopForIO::current(), io_loop_
);
270 if (!request_
->status().is_success() || bytes_read
<= 0)
272 data_
.append(buffer_
->data(), bytes_read
);
273 } while (request_
->Read(buffer_
, kRecvBufferSize
, &bytes_read
));
275 if (!request_
->status().is_io_pending()) {
278 io_loop_
->RemoveDestructionObserver(this);
280 AutoLock
autolock(lock_
);
285 Release(); // Balanced with StartURLRequest().
289 virtual void WillDestroyCurrentMessageLoop() {
290 DCHECK_EQ(MessageLoopForIO::current(), io_loop_
);
292 AutoLock
autolock(lock_
);
299 friend class base::RefCountedThreadSafe
<OCSPRequestSession
>;
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
309 // Must call this method while holding |lock_|.
310 void CancelLocked() {
311 lock_
.AssertAcquired();
315 NewRunnableMethod(this, &OCSPRequestSession::CancelURLRequest
));
319 void StartURLRequest() {
322 URLRequestContext
* url_request_context
=
323 OCSPInitSingleton::url_request_context();
324 if (url_request_context
== NULL
)
328 AutoLock
autolock(lock_
);
330 io_loop_
= MessageLoopForIO::current();
331 io_loop_
->AddDestructionObserver(this);
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
);
341 if (http_request_method_
== "POST") {
342 DCHECK(!upload_content_
.empty());
343 DCHECK(!upload_content_type_
.empty());
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()));
353 if (!extra_request_headers_
.empty())
354 request_
->SetExtraRequestHeaders(extra_request_headers_
);
357 AddRef(); // Release after |request_| deleted.
360 void CancelURLRequest() {
363 AutoLock
autolock(lock_
);
365 DCHECK_EQ(MessageLoopForIO::current(), io_loop_
);
372 // |io_loop_| may be NULL here if it called from
373 // WillDestroyCurrentMessageLoop().
375 io_loop_
->RemoveDestructionObserver(this);
377 AutoLock
autolock(lock_
);
382 Release(); // Balanced with StartURLRequest().
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
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
400 // |lock_| protects |finished_| and |io_loop_|.
402 ConditionVariable cv_
;
404 MessageLoop
* io_loop_
; // Message loop of the IO thread
407 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession
);
410 // Concrete class for SEC_HTTP_SERVER_SESSION.
411 class OCSPServerSession
{
413 OCSPServerSession(const char* host
, PRUint16 port
)
414 : host_(host
), port_(port
) {}
415 ~OCSPServerSession() {}
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)
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
,
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
)));
446 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession
);
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.";
460 *pSession
= new OCSPServerSession(host
, portnum
);
464 SECStatus
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session
,
465 PRPollDesc
**pPollDesc
) {
466 LOG(INFO
) << "OCSP keep alive";
467 DCHECK(!MessageLoop::current());
473 SECStatus
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session
) {
474 LOG(INFO
) << "OCSP free session";
475 DCHECK(!MessageLoop::current());
476 delete reinterpret_cast<OCSPServerSession
*>(session
);
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
);
494 OCSPRequestSession
* req
= ocsp_session
->CreateRequest(http_protocol_variant
,
495 path_and_query_string
,
498 SECStatus rv
= SECFailure
;
500 req
->AddRef(); // Release in OCSPFree().
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
);
515 req
->SetPostData(http_data
, http_data_len
, http_content_type
);
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
);
527 req
->AddHeader(http_header_name
, http_header_value
);
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();
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();
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.
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.
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))
597 return OCSPSetResponse(
598 req
, http_response_code
,
599 http_response_content_type
,
600 http_response_headers
,
602 http_response_data_len
) ? SECSuccess
: SECFailure
;
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;
613 SECStatus
OCSPFree(SEC_HTTP_REQUEST_SESSION request
) {
614 LOG(INFO
) << "OCSP free";
615 DCHECK(!MessageLoop::current());
616 OCSPRequestSession
* req
= reinterpret_cast<OCSPRequestSession
*>(request
);
622 } // anonymous namespace
626 void EnsureOCSPInit() {
627 Singleton
<OCSPInitSingleton
>::get();
630 // This function would be called before NSS initialization.
631 void SetURLRequestContextForOCSP(URLRequestContext
* request_context
) {
632 OCSPInitSingleton::set_url_request_context(request_context
);
635 URLRequestContext
* GetURLRequestContextForOCSP() {
636 return OCSPInitSingleton::url_request_context();