Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(719)

Side by Side Diff: net/ocsp/nss_ocsp.cc

Issue 3391028: Reland r59972: Eagerly set the IO loop used for OCSP. (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: Address wtc comments. Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/ocsp/nss_ocsp.h ('k') | net/url_request/url_request_context.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/ocsp/nss_ocsp.h" 5 #include "net/ocsp/nss_ocsp.h"
6 6
7 #include <certt.h> 7 #include <certt.h>
8 #include <certdb.h> 8 #include <certdb.h>
9 #include <ocsp.h> 9 #include <ocsp.h>
10 #include <nspr.h> 10 #include <nspr.h>
11 #include <nss.h> 11 #include <nss.h>
12 #include <pthread.h>
12 #include <secerr.h> 13 #include <secerr.h>
13 14
14 #include <string> 15 #include <string>
15 16
17 #include "base/basictypes.h"
16 #include "base/compiler_specific.h" 18 #include "base/compiler_specific.h"
17 #include "base/condition_variable.h" 19 #include "base/condition_variable.h"
18 #include "base/histogram.h" 20 #include "base/histogram.h"
21 #include "base/lazy_instance.h"
22 #include "base/lock.h"
19 #include "base/logging.h" 23 #include "base/logging.h"
20 #include "base/message_loop.h" 24 #include "base/message_loop.h"
21 #include "base/singleton.h"
22 #include "base/string_util.h" 25 #include "base/string_util.h"
23 #include "base/thread.h" 26 #include "base/thread.h"
24 #include "base/time.h" 27 #include "base/time.h"
25 #include "googleurl/src/gurl.h" 28 #include "googleurl/src/gurl.h"
26 #include "net/base/io_buffer.h" 29 #include "net/base/io_buffer.h"
27 #include "net/base/load_flags.h" 30 #include "net/base/load_flags.h"
28 #include "net/http/http_request_headers.h" 31 #include "net/http/http_request_headers.h"
29 #include "net/http/http_response_headers.h" 32 #include "net/http/http_response_headers.h"
30 #include "net/url_request/url_request.h" 33 #include "net/url_request/url_request.h"
31 #include "net/url_request/url_request_context.h" 34 #include "net/url_request/url_request_context.h"
32 35
33 namespace { 36 namespace {
34 37
38 // Protects |g_request_context|.
39 pthread_mutex_t g_request_context_lock = PTHREAD_MUTEX_INITIALIZER;
40 static URLRequestContext* g_request_context = NULL;
41
42 class OCSPIOLoop : public MessageLoop::DestructionObserver {
43 public:
44 // MessageLoop::DestructionObserver:
45 virtual void WillDestroyCurrentMessageLoop();
46
47 void StartUsing() {
48 AutoLock autolock(lock_);
49 used_ = true;
50 }
51
52 bool used() const {
53 AutoLock autolock(lock_);
54 return used_;
55 }
56
57 // Called from worker thread.
58 void PostTaskToIOLoop(const tracked_objects::Location& from_here, Task* task);
59
60 void EnsureIOLoop();
61
62 private:
63 friend struct base::DefaultLazyInstanceTraits<OCSPIOLoop>;
64
65 OCSPIOLoop();
66 ~OCSPIOLoop();
67
68 mutable Lock lock_;
69 bool used_; // Protected by |lock_|.
70 // This should not be modified after |used_|.
71 MessageLoopForIO* io_loop_; // Protected by |lock_|.
72
73 DISALLOW_COPY_AND_ASSIGN(OCSPIOLoop);
74 };
75
76 OCSPIOLoop::OCSPIOLoop()
77 : used_(false),
78 io_loop_(MessageLoopForIO::current()) {
79 DCHECK(io_loop_);
80 io_loop_->AddDestructionObserver(this);
81 }
82
83 OCSPIOLoop::~OCSPIOLoop() {
84 // IO thread was already deleted before the singleton is deleted
85 // in AtExitManager.
86 {
87 AutoLock autolock(lock_);
88 DCHECK(!io_loop_);
89 DCHECK(!used_);
90 }
91
92 pthread_mutex_lock(&g_request_context_lock);
93 DCHECK(!g_request_context);
94 pthread_mutex_unlock(&g_request_context_lock);
95 }
96
97 void OCSPIOLoop::WillDestroyCurrentMessageLoop() {
98 // Prevent the worker thread from trying to access |io_loop_|.
99 {
100 AutoLock autolock(lock_);
101 DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
102 io_loop_ = NULL;
103 used_ = false;
104 }
105
106 pthread_mutex_lock(&g_request_context_lock);
107 g_request_context = NULL;
108 pthread_mutex_unlock(&g_request_context_lock);
109 }
110
111 void OCSPIOLoop::PostTaskToIOLoop(
112 const tracked_objects::Location& from_here, Task* task) {
113 AutoLock autolock(lock_);
114 if (io_loop_)
115 io_loop_->PostTask(from_here, task);
116 }
117
118 void OCSPIOLoop::EnsureIOLoop() {
119 AutoLock autolock(lock_);
120 DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
121 }
122
123 base::LazyInstance<OCSPIOLoop> g_ocsp_io_loop(base::LINKER_INITIALIZED);
124
35 const int kRecvBufferSize = 4096; 125 const int kRecvBufferSize = 4096;
36 126
37 // All OCSP handlers should be called in the context of 127 // All OCSP handlers should be called in the context of
38 // CertVerifier's thread (i.e. worker pool, not on the I/O thread). 128 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
39 // It supports blocking mode only. 129 // It supports blocking mode only.
40 130
41 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, 131 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
42 SEC_HTTP_SERVER_SESSION* pSession); 132 SEC_HTTP_SERVER_SESSION* pSession);
43 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session, 133 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
44 PRPollDesc **pPollDesc); 134 PRPollDesc **pPollDesc);
(...skipping 16 matching lines...) Expand all
61 PRPollDesc** pPollDesc, 151 PRPollDesc** pPollDesc,
62 PRUint16* http_response_code, 152 PRUint16* http_response_code,
63 const char** http_response_content_type, 153 const char** http_response_content_type,
64 const char** http_response_headers, 154 const char** http_response_headers,
65 const char** http_response_data, 155 const char** http_response_data,
66 PRUint32* http_response_data_len); 156 PRUint32* http_response_data_len);
67 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request); 157 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
68 158
69 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert); 159 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert);
70 160
71 class OCSPInitSingleton : public MessageLoop::DestructionObserver { 161 class OCSPNSSInitialization {
72 public: 162 private:
73 // Called on IO thread. 163 friend struct base::DefaultLazyInstanceTraits<OCSPNSSInitialization>;
74 virtual void WillDestroyCurrentMessageLoop() {
75 AutoLock autolock(lock_);
76 DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
77 io_loop_ = NULL;
78 request_context_ = NULL;
79 };
80 164
81 // Called from worker thread. 165 OCSPNSSInitialization();
82 void PostTaskToIOLoop( 166 ~OCSPNSSInitialization();
83 const tracked_objects::Location& from_here, Task* task) {
84 AutoLock autolock(lock_);
85 if (io_loop_)
86 io_loop_->PostTask(from_here, task);
87 }
88
89 // This is static method because it is called before NSS initialization,
90 // that is, before OCSPInitSingleton is initialized.
91 static void set_url_request_context(URLRequestContext* request_context) {
92 request_context_ = request_context;
93 }
94 static URLRequestContext* url_request_context() {
95 return request_context_;
96 }
97
98 private:
99 friend struct DefaultSingletonTraits<OCSPInitSingleton>;
100
101 OCSPInitSingleton()
102 : io_loop_(MessageLoopForIO::current()) {
103 DCHECK(io_loop_);
104 io_loop_->AddDestructionObserver(this);
105
106 // NSS calls the functions in the function table to download certificates
107 // or CRLs or talk to OCSP responders over HTTP. These functions must
108 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
109 // residual error code from an earlier failed function call.
110 client_fcn_.version = 1;
111 SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
112 ft->createSessionFcn = OCSPCreateSession;
113 ft->keepAliveSessionFcn = OCSPKeepAliveSession;
114 ft->freeSessionFcn = OCSPFreeSession;
115 ft->createFcn = OCSPCreate;
116 ft->setPostDataFcn = OCSPSetPostData;
117 ft->addHeaderFcn = OCSPAddHeader;
118 ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
119 ft->cancelFcn = NULL;
120 ft->freeFcn = OCSPFree;
121 SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
122 if (status != SECSuccess) {
123 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
124 }
125
126 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
127 // CRLs for Network Solutions Certificate Authority have bad signatures,
128 // which causes certificates issued by that CA to be reported as revoked.
129 // By using OCSP for those certificates, which don't have AIA extensions,
130 // we can work around these bugs. See http://crbug.com/41730.
131 CERT_StringFromCertFcn old_callback = NULL;
132 status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
133 GetAlternateOCSPAIAInfo, &old_callback);
134 if (status == SECSuccess) {
135 DCHECK(!old_callback);
136 } else {
137 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
138 }
139 }
140
141 virtual ~OCSPInitSingleton() {
142 // IO thread was already deleted before the singleton is deleted
143 // in AtExitManager.
144 AutoLock autolock(lock_);
145 DCHECK(!io_loop_);
146 DCHECK(!request_context_);
147 }
148 167
149 SEC_HttpClientFcn client_fcn_; 168 SEC_HttpClientFcn client_fcn_;
150 169
151 // |lock_| protects |io_loop_|. 170 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization);
152 Lock lock_;
153 // I/O thread.
154 MessageLoop* io_loop_; // I/O thread
155 // URLRequestContext for OCSP handlers.
156 static URLRequestContext* request_context_;
157
158 DISALLOW_COPY_AND_ASSIGN(OCSPInitSingleton);
159 }; 171 };
160 172
161 URLRequestContext* OCSPInitSingleton::request_context_ = NULL; 173 OCSPNSSInitialization::OCSPNSSInitialization() {
174 // NSS calls the functions in the function table to download certificates
175 // or CRLs or talk to OCSP responders over HTTP. These functions must
176 // set an NSS/NSPR error code when they fail. Otherwise NSS will get the
177 // residual error code from an earlier failed function call.
178 client_fcn_.version = 1;
179 SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
180 ft->createSessionFcn = OCSPCreateSession;
181 ft->keepAliveSessionFcn = OCSPKeepAliveSession;
182 ft->freeSessionFcn = OCSPFreeSession;
183 ft->createFcn = OCSPCreate;
184 ft->setPostDataFcn = OCSPSetPostData;
185 ft->addHeaderFcn = OCSPAddHeader;
186 ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
187 ft->cancelFcn = NULL;
188 ft->freeFcn = OCSPFree;
189 SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
190 if (status != SECSuccess) {
191 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
192 }
193
194 // Work around NSS bugs 524013 and 564334. NSS incorrectly thinks the
195 // CRLs for Network Solutions Certificate Authority have bad signatures,
196 // which causes certificates issued by that CA to be reported as revoked.
197 // By using OCSP for those certificates, which don't have AIA extensions,
198 // we can work around these bugs. See http://crbug.com/41730.
199 CERT_StringFromCertFcn old_callback = NULL;
200 status = CERT_RegisterAlternateOCSPAIAInfoCallBack(
201 GetAlternateOCSPAIAInfo, &old_callback);
202 if (status == SECSuccess) {
203 DCHECK(!old_callback);
204 } else {
205 NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
206 }
207 }
208
209 OCSPNSSInitialization::~OCSPNSSInitialization() {}
210
211 base::LazyInstance<OCSPNSSInitialization> g_ocsp_nss_initialization(
212 base::LINKER_INITIALIZED);
162 213
163 // Concrete class for SEC_HTTP_REQUEST_SESSION. 214 // Concrete class for SEC_HTTP_REQUEST_SESSION.
164 // Public methods except virtual methods of URLRequest::Delegate (On* methods) 215 // Public methods except virtual methods of URLRequest::Delegate (On* methods)
165 // run on certificate verifier thread (worker thread). 216 // run on certificate verifier thread (worker thread).
166 // Virtual methods of URLRequest::Delegate and private methods run 217 // Virtual methods of URLRequest::Delegate and private methods run
167 // on IO thread. 218 // on IO thread.
168 class OCSPRequestSession 219 class OCSPRequestSession
169 : public base::RefCountedThreadSafe<OCSPRequestSession>, 220 : public base::RefCountedThreadSafe<OCSPRequestSession>,
170 public URLRequest::Delegate, 221 public URLRequest::Delegate,
171 public MessageLoop::DestructionObserver { 222 public MessageLoop::DestructionObserver {
(...skipping 20 matching lines...) Expand all
192 void AddHeader(const char* http_header_name, const char* http_header_value) { 243 void AddHeader(const char* http_header_name, const char* http_header_value) {
193 extra_request_headers_.SetHeader(http_header_name, 244 extra_request_headers_.SetHeader(http_header_name,
194 http_header_value); 245 http_header_value);
195 } 246 }
196 247
197 void Start() { 248 void Start() {
198 // At this point, it runs on worker thread. 249 // At this point, it runs on worker thread.
199 // |io_loop_| was initialized to be NULL in constructor, and 250 // |io_loop_| was initialized to be NULL in constructor, and
200 // set only in StartURLRequest, so no need to lock |lock_| here. 251 // set only in StartURLRequest, so no need to lock |lock_| here.
201 DCHECK(!io_loop_); 252 DCHECK(!io_loop_);
202 Singleton<OCSPInitSingleton>()->PostTaskToIOLoop( 253 g_ocsp_io_loop.Get().PostTaskToIOLoop(
203 FROM_HERE, 254 FROM_HERE,
204 NewRunnableMethod(this, &OCSPRequestSession::StartURLRequest)); 255 NewRunnableMethod(this, &OCSPRequestSession::StartURLRequest));
205 } 256 }
206 257
207 bool Started() const { 258 bool Started() const {
208 return request_ != NULL; 259 return request_ != NULL;
209 } 260 }
210 261
211 void Cancel() { 262 void Cancel() {
212 // IO thread may set |io_loop_| to NULL, so protect by |lock_|. 263 // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
331 // Must call this method while holding |lock_|. 382 // Must call this method while holding |lock_|.
332 void CancelLocked() { 383 void CancelLocked() {
333 lock_.AssertAcquired(); 384 lock_.AssertAcquired();
334 if (io_loop_) { 385 if (io_loop_) {
335 io_loop_->PostTask( 386 io_loop_->PostTask(
336 FROM_HERE, 387 FROM_HERE,
337 NewRunnableMethod(this, &OCSPRequestSession::CancelURLRequest)); 388 NewRunnableMethod(this, &OCSPRequestSession::CancelURLRequest));
338 } 389 }
339 } 390 }
340 391
392 // Runs on |g_ocsp_io_loop|'s IO loop.
341 void StartURLRequest() { 393 void StartURLRequest() {
342 DCHECK(!request_); 394 DCHECK(!request_);
343 395
344 URLRequestContext* url_request_context = 396 pthread_mutex_lock(&g_request_context_lock);
345 OCSPInitSingleton::url_request_context(); 397 URLRequestContext* url_request_context = g_request_context;
398 pthread_mutex_unlock(&g_request_context_lock);
399
346 if (url_request_context == NULL) 400 if (url_request_context == NULL)
347 return; 401 return;
348 402
349 { 403 {
350 AutoLock autolock(lock_); 404 AutoLock autolock(lock_);
351 DCHECK(!io_loop_); 405 DCHECK(!io_loop_);
352 io_loop_ = MessageLoopForIO::current(); 406 io_loop_ = MessageLoopForIO::current();
353 io_loop_->AddDestructionObserver(this); 407 io_loop_->AddDestructionObserver(this);
354 } 408 }
355 409
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession); 522 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession);
469 }; 523 };
470 524
471 525
472 // OCSP Http Client functions. 526 // OCSP Http Client functions.
473 // Our Http Client functions operate in blocking mode. 527 // Our Http Client functions operate in blocking mode.
474 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, 528 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
475 SEC_HTTP_SERVER_SESSION* pSession) { 529 SEC_HTTP_SERVER_SESSION* pSession) {
476 LOG(INFO) << "OCSP create session: host=" << host << " port=" << portnum; 530 LOG(INFO) << "OCSP create session: host=" << host << " port=" << portnum;
477 DCHECK(!MessageLoop::current()); 531 DCHECK(!MessageLoop::current());
478 if (OCSPInitSingleton::url_request_context() == NULL) { 532 pthread_mutex_lock(&g_request_context_lock);
533 URLRequestContext* request_context = g_request_context;
534 pthread_mutex_unlock(&g_request_context_lock);
535 if (request_context == NULL) {
479 LOG(ERROR) << "No URLRequestContext for OCSP handler."; 536 LOG(ERROR) << "No URLRequestContext for OCSP handler.";
480 // The application failed to call SetURLRequestContextForOCSP, so we 537 // The application failed to call SetURLRequestContextForOCSP, so we
481 // can't create and use URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an 538 // can't create and use URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an
482 // accurate error code for this error condition, but is close enough. 539 // accurate error code for this error condition, but is close enough.
483 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); 540 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
484 return SECFailure; 541 return SECFailure;
485 } 542 }
486 *pSession = new OCSPServerSession(host, portnum); 543 *pSession = new OCSPServerSession(host, portnum);
487 return SECSuccess; 544 return SECSuccess;
488 } 545 }
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after
775 } 832 }
776 } 833 }
777 834
778 return NULL; 835 return NULL;
779 } 836 }
780 837
781 } // anonymous namespace 838 } // anonymous namespace
782 839
783 namespace net { 840 namespace net {
784 841
842 void SetMessageLoopForOCSP() {
843 // Must have a MessageLoopForIO.
844 DCHECK(MessageLoopForIO::current());
845
846 bool used = g_ocsp_io_loop.Get().used();
847
848 // Should not be called when g_ocsp_io_loop has already been used.
849 DCHECK(!used);
850 }
851
785 void EnsureOCSPInit() { 852 void EnsureOCSPInit() {
786 Singleton<OCSPInitSingleton>::get(); 853 g_ocsp_io_loop.Get().StartUsing();
854 g_ocsp_nss_initialization.Get();
787 } 855 }
788 856
789 // This function would be called before NSS initialization. 857 // This function would be called before NSS initialization.
790 void SetURLRequestContextForOCSP(URLRequestContext* request_context) { 858 void SetURLRequestContextForOCSP(URLRequestContext* request_context) {
791 OCSPInitSingleton::set_url_request_context(request_context); 859 pthread_mutex_lock(&g_request_context_lock);
860 if (request_context) {
861 DCHECK(request_context->is_main());
862 DCHECK(!g_request_context);
863 } else {
864 DCHECK(g_request_context);
865 }
866 g_request_context = request_context;
867 pthread_mutex_unlock(&g_request_context_lock);
792 } 868 }
793 869
794 URLRequestContext* GetURLRequestContextForOCSP() { 870 URLRequestContext* GetURLRequestContextForOCSP() {
795 return OCSPInitSingleton::url_request_context(); 871 pthread_mutex_lock(&g_request_context_lock);
872 URLRequestContext* request_context = g_request_context;
873 pthread_mutex_unlock(&g_request_context_lock);
874 DCHECK(request_context->is_main());
875 return request_context;
796 } 876 }
797 877
798 } // namespace net 878 } // namespace net
OLDNEW
« no previous file with comments | « net/ocsp/nss_ocsp.h ('k') | net/url_request/url_request_context.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698