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:
StartUsing()48 void StartUsing() {
49 base::AutoLock autolock(lock_);
50 used_ = true;
51 }
52
53 // Called on IO loop.
54 void Shutdown();
55
used() const56 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:
OCSPRequestSession(const GURL & url,const char * http_request_method,base::TimeDelta timeout)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
SetPostData(const char * http_data,PRUint32 http_data_len,const char * http_content_type)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
AddHeader(const char * http_header_name,const char * http_header_value)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
Start()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
Started() const185 bool Started() const {
186 return request_ != NULL;
187 }
188
Cancel()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
Finished() const195 bool Finished() const {
196 base::AutoLock autolock(lock_);
197 return finished_;
198 }
199
Wait()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
url() const219 const GURL& url() const {
220 return url_;
221 }
222
http_request_method() const223 const std::string& http_request_method() const {
224 return http_request_method_;
225 }
226
timeout() const227 base::TimeDelta timeout() const {
228 return timeout_;
229 }
230
http_response_code() const231 PRUint16 http_response_code() const {
232 DCHECK(finished_);
233 return response_code_;
234 }
235
http_response_content_type() const236 const std::string& http_response_content_type() const {
237 DCHECK(finished_);
238 return response_content_type_;
239 }
240
http_response_headers() const241 const std::string& http_response_headers() const {
242 DCHECK(finished_);
243 return response_headers_->raw_headers();
244 }
245
http_response_data() const246 const std::string& http_response_data() const {
247 DCHECK(finished_);
248 return data_;
249 }
250
OnReceivedRedirect(net::URLRequest * request,const GURL & new_url,bool * defer_redirect)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
OnResponseStarted(net::URLRequest * request)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
OnReadCompleted(net::URLRequest * request,int bytes_read)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.
CancelURLRequest()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
~OCSPRequestSession()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_|.
CancelLocked()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.
StartURLRequest()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:
OCSPServerSession(const char * host,PRUint16 port)416 OCSPServerSession(const char* host, PRUint16 port)
417 : host_(host), port_(port) {}
~OCSPServerSession()418 ~OCSPServerSession() {}
419
CreateRequest(const char * http_protocol_variant,const char * path_and_query_string,const char * http_request_method,const PRIntervalTime timeout)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
OCSPIOLoop()454 OCSPIOLoop::OCSPIOLoop()
455 : shutdown_(false),
456 used_(false),
457 io_loop_(MessageLoopForIO::current()) {
458 DCHECK(io_loop_);
459 }
460
~OCSPIOLoop()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
Shutdown()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
PostTaskToIOLoop(const tracked_objects::Location & from_here,Task * task)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
EnsureIOLoop()502 void OCSPIOLoop::EnsureIOLoop() {
503 base::AutoLock autolock(lock_);
504 DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
505 }
506
AddRequest(OCSPRequestSession * request)507 void OCSPIOLoop::AddRequest(OCSPRequestSession* request) {
508 DCHECK(!ContainsKey(requests_, request));
509 requests_.insert(request);
510 }
511
RemoveRequest(OCSPRequestSession * request)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
CancelAllRequests()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
OCSPNSSInitialization()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
~OCSPNSSInitialization()569 OCSPNSSInitialization::~OCSPNSSInitialization() {}
570
571
572 // OCSP Http Client functions.
573 // Our Http Client functions operate in blocking mode.
OCSPCreateSession(const char * host,PRUint16 portnum,SEC_HTTP_SERVER_SESSION * pSession)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
OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,PRPollDesc ** pPollDesc)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
OCSPFreeSession(SEC_HTTP_SERVER_SESSION session)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
OCSPCreate(SEC_HTTP_SERVER_SESSION session,const char * http_protocol_variant,const char * path_and_query_string,const char * http_request_method,const PRIntervalTime timeout,SEC_HTTP_REQUEST_SESSION * pRequest)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
OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,const char * http_data,const PRUint32 http_data_len,const char * http_content_type)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
OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,const char * http_header_name,const char * http_header_value)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|.
OCSPSetResponse(OCSPRequestSession * req,PRUint16 * http_response_code,const char ** http_response_content_type,const char ** http_response_headers,const char ** http_response_data,PRUint32 * http_response_data_len)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
OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,PRPollDesc ** pPollDesc,PRUint16 * http_response_code,const char ** http_response_content_type,const char ** http_response_headers,const char ** http_response_data,PRUint32 * http_response_data_len)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
OCSPFree(SEC_HTTP_REQUEST_SESSION request)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
GetAlternateOCSPAIAInfo(CERTCertificate * cert)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
SetMessageLoopForOCSP()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
EnsureOCSPInit()890 void EnsureOCSPInit() {
891 g_ocsp_io_loop.Get().StartUsing();
892 g_ocsp_nss_initialization.Get();
893 }
894
ShutdownOCSP()895 void ShutdownOCSP() {
896 g_ocsp_io_loop.Get().Shutdown();
897 }
898
899 // This function would be called before NSS initialization.
SetURLRequestContextForOCSP(URLRequestContext * request_context)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
GetURLRequestContextForOCSP()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