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