OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |