Update expectations after WebKit roll.
[chromium-blink-merge.git] / net / ocsp / nss_ocsp.cc
blobc135dfd92f57d2057e444d4f8a0de68422e11d76
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"
7 #include <certt.h>
8 #include <certdb.h>
9 #include <ocsp.h>
10 #include <nspr.h>
11 #include <nss.h>
12 #include <secerr.h>
14 #include <string>
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"
30 namespace {
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 {
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;
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);
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_;
93 private:
94 friend struct DefaultSingletonTraits<OCSPInitSingleton>;
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();
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_);
125 SEC_HttpClientFcn client_fcn_;
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_;
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
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) {}
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);
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));
185 bool Started() const {
186 return request_ != NULL;
189 void Cancel() {
190 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
191 AutoLock autolock(lock_);
192 CancelLocked();
195 bool Finished() const {
196 AutoLock autolock(lock_);
197 return finished_;
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;
216 return finished_;
219 const GURL& url() const {
220 return url_;
223 const std::string& http_request_method() const {
224 return http_request_method_;
227 base::TimeDelta timeout() const {
228 return timeout_;
231 PRUint16 http_response_code() const {
232 DCHECK(finished_);
233 return response_code_;
236 const std::string& http_response_content_type() const {
237 DCHECK(finished_);
238 return response_content_type_;
241 const std::string& http_response_headers() const {
242 DCHECK(finished_);
243 return response_headers_->raw_headers();
246 const std::string& http_response_data() const {
247 DCHECK(finished_);
248 return data_;
251 virtual void OnResponseStarted(URLRequest* request) {
252 DCHECK_EQ(request, request_);
253 DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
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);
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_);
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));
275 if (!request_->status().is_io_pending()) {
276 delete request_;
277 request_ = NULL;
278 io_loop_->RemoveDestructionObserver(this);
280 AutoLock autolock(lock_);
281 finished_ = true;
282 io_loop_ = NULL;
284 cv_.Signal();
285 Release(); // Balanced with StartURLRequest().
289 virtual void WillDestroyCurrentMessageLoop() {
290 DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
292 AutoLock autolock(lock_);
293 io_loop_ = NULL;
295 CancelURLRequest();
298 private:
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
304 // |lock_| here.
305 DCHECK(!request_);
306 DCHECK(!io_loop_);
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));
319 void StartURLRequest() {
320 DCHECK(!request_);
322 URLRequestContext* url_request_context =
323 OCSPInitSingleton::url_request_context();
324 if (url_request_context == NULL)
325 return;
328 AutoLock autolock(lock_);
329 DCHECK(!io_loop_);
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_);
356 request_->Start();
357 AddRef(); // Release after |request_| deleted.
360 void CancelURLRequest() {
361 #ifndef NDEBUG
363 AutoLock autolock(lock_);
364 if (io_loop_)
365 DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
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);
377 AutoLock autolock(lock_);
378 finished_ = true;
379 io_loop_ = NULL;
381 cv_.Signal();
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_|.
401 mutable Lock lock_;
402 ConditionVariable cv_;
404 MessageLoop* io_loop_; // Message loop of the IO thread
405 bool finished_;
407 DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession);
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() {}
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;
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)));
442 private:
443 std::string host_;
444 int port_;
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.";
458 return SECFailure;
460 *pSession = new OCSPServerSession(host, portnum);
461 return SECSuccess;
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;
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;
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,
496 http_request_method,
497 timeout);
498 SECStatus rv = SECFailure;
499 if (req) {
500 req->AddRef(); // Release in OCSPFree().
501 rv = SECSuccess;
503 *pRequest = req;
504 return rv;
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);
516 return SECSuccess;
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);
528 return SECSuccess;
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;
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;
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;
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;
589 req->Start();
590 if (!req->Wait())
591 goto failed;
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;
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;
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;
610 return SECFailure;
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;
622 } // anonymous namespace
624 namespace net {
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();
639 } // namespace net