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> | |
13 #include <secerr.h> | 12 #include <secerr.h> |
14 | 13 |
15 #include <string> | 14 #include <string> |
16 | 15 |
17 #include "base/basictypes.h" | |
18 #include "base/compiler_specific.h" | 16 #include "base/compiler_specific.h" |
19 #include "base/condition_variable.h" | 17 #include "base/condition_variable.h" |
20 #include "base/histogram.h" | 18 #include "base/histogram.h" |
21 #include "base/lazy_instance.h" | |
22 #include "base/lock.h" | |
23 #include "base/logging.h" | 19 #include "base/logging.h" |
24 #include "base/message_loop.h" | 20 #include "base/message_loop.h" |
| 21 #include "base/singleton.h" |
25 #include "base/string_util.h" | 22 #include "base/string_util.h" |
26 #include "base/stringprintf.h" | 23 #include "base/stringprintf.h" |
27 #include "base/thread.h" | 24 #include "base/thread.h" |
28 #include "base/time.h" | 25 #include "base/time.h" |
29 #include "googleurl/src/gurl.h" | 26 #include "googleurl/src/gurl.h" |
30 #include "net/base/io_buffer.h" | 27 #include "net/base/io_buffer.h" |
31 #include "net/base/load_flags.h" | 28 #include "net/base/load_flags.h" |
32 #include "net/http/http_request_headers.h" | 29 #include "net/http/http_request_headers.h" |
33 #include "net/http/http_response_headers.h" | 30 #include "net/http/http_response_headers.h" |
34 #include "net/url_request/url_request.h" | 31 #include "net/url_request/url_request.h" |
35 #include "net/url_request/url_request_context.h" | 32 #include "net/url_request/url_request_context.h" |
36 | 33 |
37 namespace { | 34 namespace { |
38 | 35 |
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 | |
126 const int kRecvBufferSize = 4096; | 36 const int kRecvBufferSize = 4096; |
127 | 37 |
128 // All OCSP handlers should be called in the context of | 38 // All OCSP handlers should be called in the context of |
129 // CertVerifier's thread (i.e. worker pool, not on the I/O thread). | 39 // CertVerifier's thread (i.e. worker pool, not on the I/O thread). |
130 // It supports blocking mode only. | 40 // It supports blocking mode only. |
131 | 41 |
132 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, | 42 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, |
133 SEC_HTTP_SERVER_SESSION* pSession); | 43 SEC_HTTP_SERVER_SESSION* pSession); |
134 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session, | 44 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session, |
135 PRPollDesc **pPollDesc); | 45 PRPollDesc **pPollDesc); |
(...skipping 16 matching lines...) Expand all Loading... |
152 PRPollDesc** pPollDesc, | 62 PRPollDesc** pPollDesc, |
153 PRUint16* http_response_code, | 63 PRUint16* http_response_code, |
154 const char** http_response_content_type, | 64 const char** http_response_content_type, |
155 const char** http_response_headers, | 65 const char** http_response_headers, |
156 const char** http_response_data, | 66 const char** http_response_data, |
157 PRUint32* http_response_data_len); | 67 PRUint32* http_response_data_len); |
158 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request); | 68 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request); |
159 | 69 |
160 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert); | 70 char* GetAlternateOCSPAIAInfo(CERTCertificate *cert); |
161 | 71 |
162 class OCSPNSSInitialization { | 72 class OCSPInitSingleton : public MessageLoop::DestructionObserver { |
| 73 public: |
| 74 // Called on IO thread. |
| 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 |
| 82 // Called from worker thread. |
| 83 void PostTaskToIOLoop( |
| 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 |
163 private: | 99 private: |
164 friend struct base::DefaultLazyInstanceTraits<OCSPNSSInitialization>; | 100 friend struct DefaultSingletonTraits<OCSPInitSingleton>; |
165 | 101 |
166 OCSPNSSInitialization(); | 102 OCSPInitSingleton() |
167 ~OCSPNSSInitialization(); | 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 } |
168 | 149 |
169 SEC_HttpClientFcn client_fcn_; | 150 SEC_HttpClientFcn client_fcn_; |
170 | 151 |
171 DISALLOW_COPY_AND_ASSIGN(OCSPNSSInitialization); | 152 // |lock_| protects |io_loop_|. |
| 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); |
172 }; | 160 }; |
173 | 161 |
174 OCSPNSSInitialization::OCSPNSSInitialization() { | 162 URLRequestContext* OCSPInitSingleton::request_context_ = NULL; |
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); | |
214 | 163 |
215 // Concrete class for SEC_HTTP_REQUEST_SESSION. | 164 // Concrete class for SEC_HTTP_REQUEST_SESSION. |
216 // Public methods except virtual methods of URLRequest::Delegate (On* methods) | 165 // Public methods except virtual methods of URLRequest::Delegate (On* methods) |
217 // run on certificate verifier thread (worker thread). | 166 // run on certificate verifier thread (worker thread). |
218 // Virtual methods of URLRequest::Delegate and private methods run | 167 // Virtual methods of URLRequest::Delegate and private methods run |
219 // on IO thread. | 168 // on IO thread. |
220 class OCSPRequestSession | 169 class OCSPRequestSession |
221 : public base::RefCountedThreadSafe<OCSPRequestSession>, | 170 : public base::RefCountedThreadSafe<OCSPRequestSession>, |
222 public URLRequest::Delegate, | 171 public URLRequest::Delegate, |
223 public MessageLoop::DestructionObserver { | 172 public MessageLoop::DestructionObserver { |
(...skipping 20 matching lines...) Expand all Loading... |
244 void AddHeader(const char* http_header_name, const char* http_header_value) { | 193 void AddHeader(const char* http_header_name, const char* http_header_value) { |
245 extra_request_headers_.SetHeader(http_header_name, | 194 extra_request_headers_.SetHeader(http_header_name, |
246 http_header_value); | 195 http_header_value); |
247 } | 196 } |
248 | 197 |
249 void Start() { | 198 void Start() { |
250 // At this point, it runs on worker thread. | 199 // At this point, it runs on worker thread. |
251 // |io_loop_| was initialized to be NULL in constructor, and | 200 // |io_loop_| was initialized to be NULL in constructor, and |
252 // set only in StartURLRequest, so no need to lock |lock_| here. | 201 // set only in StartURLRequest, so no need to lock |lock_| here. |
253 DCHECK(!io_loop_); | 202 DCHECK(!io_loop_); |
254 g_ocsp_io_loop.Get().PostTaskToIOLoop( | 203 Singleton<OCSPInitSingleton>()->PostTaskToIOLoop( |
255 FROM_HERE, | 204 FROM_HERE, |
256 NewRunnableMethod(this, &OCSPRequestSession::StartURLRequest)); | 205 NewRunnableMethod(this, &OCSPRequestSession::StartURLRequest)); |
257 } | 206 } |
258 | 207 |
259 bool Started() const { | 208 bool Started() const { |
260 return request_ != NULL; | 209 return request_ != NULL; |
261 } | 210 } |
262 | 211 |
263 void Cancel() { | 212 void Cancel() { |
264 // IO thread may set |io_loop_| to NULL, so protect by |lock_|. | 213 // 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... |
383 // Must call this method while holding |lock_|. | 332 // Must call this method while holding |lock_|. |
384 void CancelLocked() { | 333 void CancelLocked() { |
385 lock_.AssertAcquired(); | 334 lock_.AssertAcquired(); |
386 if (io_loop_) { | 335 if (io_loop_) { |
387 io_loop_->PostTask( | 336 io_loop_->PostTask( |
388 FROM_HERE, | 337 FROM_HERE, |
389 NewRunnableMethod(this, &OCSPRequestSession::CancelURLRequest)); | 338 NewRunnableMethod(this, &OCSPRequestSession::CancelURLRequest)); |
390 } | 339 } |
391 } | 340 } |
392 | 341 |
393 // Runs on |g_ocsp_io_loop|'s IO loop. | |
394 void StartURLRequest() { | 342 void StartURLRequest() { |
395 DCHECK(!request_); | 343 DCHECK(!request_); |
396 | 344 |
397 pthread_mutex_lock(&g_request_context_lock); | 345 URLRequestContext* url_request_context = |
398 URLRequestContext* url_request_context = g_request_context; | 346 OCSPInitSingleton::url_request_context(); |
399 pthread_mutex_unlock(&g_request_context_lock); | |
400 | |
401 if (url_request_context == NULL) | 347 if (url_request_context == NULL) |
402 return; | 348 return; |
403 | 349 |
404 { | 350 { |
405 AutoLock autolock(lock_); | 351 AutoLock autolock(lock_); |
406 DCHECK(!io_loop_); | 352 DCHECK(!io_loop_); |
407 io_loop_ = MessageLoopForIO::current(); | 353 io_loop_ = MessageLoopForIO::current(); |
408 io_loop_->AddDestructionObserver(this); | 354 io_loop_->AddDestructionObserver(this); |
409 } | 355 } |
410 | 356 |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
523 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession); | 469 DISALLOW_COPY_AND_ASSIGN(OCSPServerSession); |
524 }; | 470 }; |
525 | 471 |
526 | 472 |
527 // OCSP Http Client functions. | 473 // OCSP Http Client functions. |
528 // Our Http Client functions operate in blocking mode. | 474 // Our Http Client functions operate in blocking mode. |
529 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, | 475 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum, |
530 SEC_HTTP_SERVER_SESSION* pSession) { | 476 SEC_HTTP_SERVER_SESSION* pSession) { |
531 LOG(INFO) << "OCSP create session: host=" << host << " port=" << portnum; | 477 LOG(INFO) << "OCSP create session: host=" << host << " port=" << portnum; |
532 DCHECK(!MessageLoop::current()); | 478 DCHECK(!MessageLoop::current()); |
533 pthread_mutex_lock(&g_request_context_lock); | 479 if (OCSPInitSingleton::url_request_context() == NULL) { |
534 URLRequestContext* request_context = g_request_context; | |
535 pthread_mutex_unlock(&g_request_context_lock); | |
536 if (request_context == NULL) { | |
537 LOG(ERROR) << "No URLRequestContext for OCSP handler."; | 480 LOG(ERROR) << "No URLRequestContext for OCSP handler."; |
538 // The application failed to call SetURLRequestContextForOCSP, so we | 481 // The application failed to call SetURLRequestContextForOCSP, so we |
539 // can't create and use URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an | 482 // can't create and use URLRequest. PR_NOT_IMPLEMENTED_ERROR is not an |
540 // accurate error code for this error condition, but is close enough. | 483 // accurate error code for this error condition, but is close enough. |
541 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); | 484 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR); |
542 return SECFailure; | 485 return SECFailure; |
543 } | 486 } |
544 *pSession = new OCSPServerSession(host, portnum); | 487 *pSession = new OCSPServerSession(host, portnum); |
545 return SECSuccess; | 488 return SECSuccess; |
546 } | 489 } |
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
833 } | 776 } |
834 } | 777 } |
835 | 778 |
836 return NULL; | 779 return NULL; |
837 } | 780 } |
838 | 781 |
839 } // anonymous namespace | 782 } // anonymous namespace |
840 | 783 |
841 namespace net { | 784 namespace net { |
842 | 785 |
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 | |
853 void EnsureOCSPInit() { | 786 void EnsureOCSPInit() { |
854 g_ocsp_io_loop.Get().StartUsing(); | 787 Singleton<OCSPInitSingleton>::get(); |
855 g_ocsp_nss_initialization.Get(); | |
856 } | 788 } |
857 | 789 |
858 // This function would be called before NSS initialization. | 790 // This function would be called before NSS initialization. |
859 void SetURLRequestContextForOCSP(URLRequestContext* request_context) { | 791 void SetURLRequestContextForOCSP(URLRequestContext* request_context) { |
860 pthread_mutex_lock(&g_request_context_lock); | 792 OCSPInitSingleton::set_url_request_context(request_context); |
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); | |
869 } | 793 } |
870 | 794 |
871 URLRequestContext* GetURLRequestContextForOCSP() { | 795 URLRequestContext* GetURLRequestContextForOCSP() { |
872 pthread_mutex_lock(&g_request_context_lock); | 796 return OCSPInitSingleton::url_request_context(); |
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; | |
877 } | 797 } |
878 | 798 |
879 } // namespace net | 799 } // namespace net |
OLD | NEW |