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

Side by Side Diff: net/base/ssl_client_socket_nss.cc

Issue 43115: Change the bad-certificate handler for SSL (using NSS) to return an... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 9 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 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/base/ssl_client_socket_nss.h" 5 #include "net/base/ssl_client_socket_nss.h"
6 6
7 #include <nspr.h> 7 #include <nspr.h>
8 #include <nss.h> 8 #include <nss.h>
9 #include <secerr.h> 9 #include <secerr.h>
10 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 10 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424
11 // until NSS 3.12.2 comes out and we update to it. 11 // until NSS 3.12.2 comes out and we update to it.
12 #define Lock FOO_NSS_Lock 12 #define Lock FOO_NSS_Lock
13 #include <ssl.h> 13 #include <ssl.h>
14 #include <sslerr.h> 14 #include <sslerr.h>
15 #include <pk11pub.h> 15 #include <pk11pub.h>
16 #undef Lock 16 #undef Lock
17 17
18 #include "base/compiler_specific.h"
18 #include "base/logging.h" 19 #include "base/logging.h"
19 #include "base/nss_init.h" 20 #include "base/nss_init.h"
20 #include "base/string_util.h" 21 #include "base/string_util.h"
21 #include "net/base/net_errors.h" 22 #include "net/base/net_errors.h"
22 #include "net/base/ssl_info.h" 23 #include "net/base/ssl_info.h"
23 24
24 static const int kRecvBufferSize = 4096; 25 static const int kRecvBufferSize = 4096;
25 26
26 namespace net { 27 namespace net {
27 28
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 123
123 int SSLClientSocketNSS::Init() { 124 int SSLClientSocketNSS::Init() {
124 EnterFunction(""); 125 EnterFunction("");
125 // Call NSS_NoDB_Init() in a threadsafe way. 126 // Call NSS_NoDB_Init() in a threadsafe way.
126 base::EnsureNSSInit(); 127 base::EnsureNSSInit();
127 128
128 LeaveFunction(""); 129 LeaveFunction("");
129 return OK; 130 return OK;
130 } 131 }
131 132
133 // As part of Connect(), the SSLClientSocketNSS object performs an SSL
134 // handshake. This requires network IO, which in turn calls
135 // BufferRecvComplete() with a non-zero byte count. This byte count eventually
136 // winds its way through the state machine and ends up being passed to the
137 // callback. For Read() and Write(), that's what we want. But for Connect(),
138 // the caller expects OK (i.e. 0) for success.
139 //
140 // The ConnectCallbackWrapper object changes the argument that gets passed
141 // to the callback function. Any positive value gets turned into OK.
142 class ConnectCallbackWrapper :
143 public CompletionCallbackImpl<ConnectCallbackWrapper> {
144 public:
145 ConnectCallbackWrapper(CompletionCallback* user_callback)
146 : ALLOW_THIS_IN_INITIALIZER_LIST(
147 CompletionCallbackImpl<ConnectCallbackWrapper>(this,
148 &ConnectCallbackWrapper::ReturnValueWrapper)),
149 user_callback_(user_callback) {
150 }
151
152 private:
153 void ReturnValueWrapper(int rv) {
154 user_callback_->Run(rv > OK ? OK : rv);
155 delete this;
156 }
157
158 CompletionCallback* user_callback_;
159 };
160
132 int SSLClientSocketNSS::Connect(CompletionCallback* callback) { 161 int SSLClientSocketNSS::Connect(CompletionCallback* callback) {
133 EnterFunction(""); 162 EnterFunction("");
134 DCHECK(transport_.get()); 163 DCHECK(transport_.get());
135 DCHECK(next_state_ == STATE_NONE); 164 DCHECK(next_state_ == STATE_NONE);
136 DCHECK(!user_callback_); 165 DCHECK(!user_callback_);
137 166
138 GotoState(STATE_CONNECT); 167 GotoState(STATE_CONNECT);
139 int rv = DoLoop(OK); 168 int rv = DoLoop(OK);
140 if (rv == ERR_IO_PENDING) 169 if (rv == ERR_IO_PENDING)
141 user_callback_ = callback; 170 user_callback_ = new ConnectCallbackWrapper(callback);
142 171
143 LeaveFunction(""); 172 LeaveFunction("");
144 return rv; 173 return rv > OK ? OK : rv;
145 } 174 }
146 175
147 int SSLClientSocketNSS::ReconnectIgnoringLastError( 176 void SSLClientSocketNSS::InvalidateSessionIfBadCertificate() {
148 CompletionCallback* callback) { 177 if (UpdateServerCert() != NULL &&
149 EnterFunction(""); 178 ssl_config_.allowed_bad_certs_.count(server_cert_)) {
150 // TODO(darin): implement me! 179 SSL_InvalidateSession(nss_fd_);
151 LeaveFunction(""); 180 }
152 return ERR_FAILED;
153 } 181 }
154 182
155 void SSLClientSocketNSS::Disconnect() { 183 void SSLClientSocketNSS::Disconnect() {
156 EnterFunction(""); 184 EnterFunction("");
185
186 // Reset object state
187 transport_send_busy_ = false;
188 transport_recv_busy_ = false;
189 user_buf_ = NULL;
190 user_buf_len_ = 0;
191 server_cert_error_ = OK;
192 completed_handshake_ = false;
193 nss_bufs_ = NULL;
194
157 // TODO(wtc): Send SSL close_notify alert. 195 // TODO(wtc): Send SSL close_notify alert.
158 if (nss_fd_ != NULL) { 196 if (nss_fd_ != NULL) {
197 InvalidateSessionIfBadCertificate();
159 PR_Close(nss_fd_); 198 PR_Close(nss_fd_);
160 nss_fd_ = NULL; 199 nss_fd_ = NULL;
161 } 200 }
162 completed_handshake_ = false; 201
163 transport_->Disconnect(); 202 transport_->Disconnect();
164 LeaveFunction(""); 203 LeaveFunction("");
165 } 204 }
166 205
167 bool SSLClientSocketNSS::IsConnected() const { 206 bool SSLClientSocketNSS::IsConnected() const {
168 // Ideally, we should also check if we have received the close_notify alert 207 // Ideally, we should also check if we have received the close_notify alert
169 // message from the server, and return false in that case. We're not doing 208 // message from the server, and return false in that case. We're not doing
170 // that, so this function may return a false positive. Since the upper 209 // that, so this function may return a false positive. Since the upper
171 // layer (HttpNetworkTransaction) needs to handle a persistent connection 210 // layer (HttpNetworkTransaction) needs to handle a persistent connection
172 // closed by the server when we send a request anyway, a false positive in 211 // closed by the server when we send a request anyway, a false positive in
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
222 user_buf_len_ = buf_len; 261 user_buf_len_ = buf_len;
223 262
224 GotoState(STATE_PAYLOAD_WRITE); 263 GotoState(STATE_PAYLOAD_WRITE);
225 int rv = DoLoop(OK); 264 int rv = DoLoop(OK);
226 if (rv == ERR_IO_PENDING) 265 if (rv == ERR_IO_PENDING)
227 user_callback_ = callback; 266 user_callback_ = callback;
228 LeaveFunction(rv); 267 LeaveFunction(rv);
229 return rv; 268 return rv;
230 } 269 }
231 270
271 X509Certificate *SSLClientSocketNSS::UpdateServerCert() {
272 // We set the server_cert_ from OwnAuthCertHandler(), but this handler
273 // does not necessarily get called if we are continuing a cached SSL
274 // session.
275 if (server_cert_ == NULL) {
276 X509Certificate::OSCertHandle nss_cert = SSL_PeerCertificate(nss_fd_);
277 if (nss_cert) {
278 server_cert_ = X509Certificate::CreateFromHandle(
279 nss_cert, X509Certificate::SOURCE_FROM_NETWORK);
280 }
281 }
282 return server_cert_;
283 }
284
232 void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { 285 void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
233 EnterFunction(""); 286 EnterFunction("");
234 ssl_info->Reset(); 287 ssl_info->Reset();
235 SSLChannelInfo channel_info; 288 SSLChannelInfo channel_info;
236 SECStatus ok = SSL_GetChannelInfo(nss_fd_, 289 SECStatus ok = SSL_GetChannelInfo(nss_fd_,
237 &channel_info, sizeof(channel_info)); 290 &channel_info, sizeof(channel_info));
238 if (ok == SECSuccess && 291 if (ok == SECSuccess &&
239 channel_info.length == sizeof(channel_info) && 292 channel_info.length == sizeof(channel_info) &&
240 channel_info.cipherSuite) { 293 channel_info.cipherSuite) {
241 SSLCipherSuiteInfo cipher_info; 294 SSLCipherSuiteInfo cipher_info;
242 ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, 295 ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite,
243 &cipher_info, sizeof(cipher_info)); 296 &cipher_info, sizeof(cipher_info));
244 if (ok == SECSuccess) { 297 if (ok == SECSuccess) {
245 ssl_info->security_bits = cipher_info.effectiveKeyBits; 298 ssl_info->security_bits = cipher_info.effectiveKeyBits;
246 } else { 299 } else {
247 ssl_info->security_bits = -1; 300 ssl_info->security_bits = -1;
248 LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError() 301 LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError()
249 << " for cipherSuite " << channel_info.cipherSuite; 302 << " for cipherSuite " << channel_info.cipherSuite;
250 } 303 }
304 UpdateServerCert();
251 } 305 }
252 if (server_cert_error_ != net::OK) 306 if (server_cert_error_ != net::OK)
253 ssl_info->SetCertError(server_cert_error_); 307 ssl_info->SetCertError(server_cert_error_);
254 X509Certificate::OSCertHandle nss_cert = SSL_PeerCertificate(nss_fd_); 308 DCHECK(server_cert_ != NULL);
255 if (nss_cert) 309 ssl_info->cert = server_cert_;
256 ssl_info->cert = X509Certificate::CreateFromHandle(nss_cert,
257 X509Certificate::SOURCE_FROM_NETWORK);
258 LeaveFunction(""); 310 LeaveFunction("");
259 } 311 }
260 312
261 void SSLClientSocketNSS::DoCallback(int rv) { 313 void SSLClientSocketNSS::DoCallback(int rv) {
262 EnterFunction(rv); 314 EnterFunction(rv);
263 DCHECK(rv != ERR_IO_PENDING); 315 DCHECK(rv != ERR_IO_PENDING);
264 DCHECK(user_callback_); 316 DCHECK(user_callback_);
265 317
266 // since Run may result in Read being called, clear user_callback_ up front. 318 // since Run may result in Read being called, clear user_callback_ up front.
267 CompletionCallback* c = user_callback_; 319 CompletionCallback* c = user_callback_;
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
348 } 400 }
349 401
350 void SSLClientSocketNSS::BufferRecvComplete(int result) { 402 void SSLClientSocketNSS::BufferRecvComplete(int result) {
351 EnterFunction(result); 403 EnterFunction(result);
352 memio_PutReadResult(nss_bufs_, result); 404 memio_PutReadResult(nss_bufs_, result);
353 transport_recv_busy_ = false; 405 transport_recv_busy_ = false;
354 OnIOComplete(result); 406 OnIOComplete(result);
355 LeaveFunction(""); 407 LeaveFunction("");
356 } 408 }
357 409
358
359 int SSLClientSocketNSS::DoLoop(int last_io_result) { 410 int SSLClientSocketNSS::DoLoop(int last_io_result) {
360 EnterFunction(last_io_result); 411 EnterFunction(last_io_result);
361 bool network_moved; 412 bool network_moved;
362 int rv = last_io_result; 413 int rv = last_io_result;
363 do { 414 do {
364 network_moved = false; 415 network_moved = false;
365 // Default to STATE_NONE for next state. 416 // Default to STATE_NONE for next state.
366 // (This is a quirk carried over from the windows 417 // (This is a quirk carried over from the windows
367 // implementation. It makes reading the logs a bit harder.) 418 // implementation. It makes reading the logs a bit harder.)
368 // State handlers can and often do call GotoState just 419 // State handlers can and often do call GotoState just
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
402 } 453 }
403 } while ((rv != ERR_IO_PENDING || network_moved) && 454 } while ((rv != ERR_IO_PENDING || network_moved) &&
404 next_state_ != STATE_NONE); 455 next_state_ != STATE_NONE);
405 LeaveFunction(""); 456 LeaveFunction("");
406 return rv; 457 return rv;
407 } 458 }
408 459
409 int SSLClientSocketNSS::DoConnect() { 460 int SSLClientSocketNSS::DoConnect() {
410 EnterFunction(""); 461 EnterFunction("");
411 GotoState(STATE_CONNECT_COMPLETE); 462 GotoState(STATE_CONNECT_COMPLETE);
412 return transport_->Connect(&io_callback_); 463
464 // The caller has to make sure that the transport socket is connected. If
465 // it isn't, we will eventually fail when trying to negotiate an SSL session.
466 // But we cannot call transport_->Connect(), as we do not know if there is
467 // any proxy negotiation that needs to be performed prior to establishing
468 // the SSL session.
469 return OK;
470 }
471
472 // static
473 // NSS calls this if an incoming certificate needs to be verified.
474 SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg,
475 PRFileDesc* socket,
476 PRBool checksig,
477 PRBool is_server) {
478 SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg);
479
480 // Remember the certificate as it will no longer be accessible if the
481 // handshake fails.
482 that->UpdateServerCert();
483
484 return SSL_AuthCertificate(CERT_GetDefaultCertDB(), socket, checksig,
485 is_server);
413 } 486 }
414 487
415 // static 488 // static
416 // NSS calls this if an incoming certificate is invalid. 489 // NSS calls this if an incoming certificate is invalid.
417 SECStatus SSLClientSocketNSS::OwnBadCertHandler(void* arg, PRFileDesc* socket) { 490 SECStatus SSLClientSocketNSS::OwnBadCertHandler(void* arg,
491 PRFileDesc* socket) {
418 SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); 492 SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg);
493
494 if (that->server_cert_ &&
495 that->ssl_config_.allowed_bad_certs_.count(that->server_cert_)) {
496 LOG(INFO) << "accepting bad SSL certificate, as user told us to";
497
498 return SECSuccess;
499 }
419 PRErrorCode prerr = PR_GetError(); 500 PRErrorCode prerr = PR_GetError();
420 that->server_cert_error_ = NetErrorFromNSPRError(prerr); 501 that->server_cert_error_ = NetErrorFromNSPRError(prerr);
421 LOG(INFO) << "server certificate is invalid; NSS error code " << prerr 502 LOG(INFO) << "server certificate is invalid; NSS error code " << prerr
422 << ", net error " << that->server_cert_error_; 503 << ", net error " << that->server_cert_error_;
423 // Return SECSuccess to override the problem. 504
424 // Chromium wants it to succeed here, and may abort the connection later. 505 return SECFailure;
425 return SECSuccess;
426 } 506 }
427 507
428 int SSLClientSocketNSS::DoConnectComplete(int result) { 508 int SSLClientSocketNSS::DoConnectComplete(int result) {
429 EnterFunction(result); 509 EnterFunction(result);
430 if (result < 0) 510 if (result < 0)
431 return result; 511 return result;
432 512
433 if (Init() != OK) { 513 if (Init() != OK) {
434 NOTREACHED() << "Couldn't initialize nss"; 514 NOTREACHED() << "Couldn't initialize nss";
435 } 515 }
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 if (rv != SECSuccess) 576 if (rv != SECSuccess)
497 LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS failed. Old system nss?"; 577 LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS failed. Old system nss?";
498 #else 578 #else
499 #error "You need to install NSS-3.12 or later to build chromium" 579 #error "You need to install NSS-3.12 or later to build chromium"
500 #endif 580 #endif
501 581
502 rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); 582 rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
503 if (rv != SECSuccess) 583 if (rv != SECSuccess)
504 return ERR_UNEXPECTED; 584 return ERR_UNEXPECTED;
505 585
586 rv = SSL_AuthCertificateHook(nss_fd_, OwnAuthCertHandler, this);
587 if (rv != SECSuccess)
588 return ERR_UNEXPECTED;
589
506 rv = SSL_BadCertHook(nss_fd_, OwnBadCertHandler, this); 590 rv = SSL_BadCertHook(nss_fd_, OwnBadCertHandler, this);
507 if (rv != SECSuccess) 591 if (rv != SECSuccess)
508 return ERR_UNEXPECTED; 592 return ERR_UNEXPECTED;
509 593
510 // Tell SSL the hostname we're trying to connect to. 594 // Tell SSL the hostname we're trying to connect to.
511 SSL_SetURL(nss_fd_, hostname_.c_str()); 595 SSL_SetURL(nss_fd_, hostname_.c_str());
512 596
513 // Tell SSL we're a client; needed if not letting NSPR do socket I/O 597 // Tell SSL we're a client; needed if not letting NSPR do socket I/O
514 SSL_ResetHandshake(nss_fd_, 0); 598 SSL_ResetHandshake(nss_fd_, 0);
515 GotoState(STATE_HANDSHAKE_READ); 599 GotoState(STATE_HANDSHAKE_READ);
516 // Return OK so DoLoop tries handshaking 600 // Return OK so DoLoop tries handshaking
517 LeaveFunction(""); 601 LeaveFunction("");
518 return OK; 602 return OK;
519 } 603 }
520 604
521 int SSLClientSocketNSS::DoHandshakeRead() { 605 int SSLClientSocketNSS::DoHandshakeRead() {
522 EnterFunction(""); 606 EnterFunction("");
523 int net_error; 607 int net_error = net::OK;
524 int rv = SSL_ForceHandshake(nss_fd_); 608 int rv = SSL_ForceHandshake(nss_fd_);
525 609
526 if (rv == SECSuccess) { 610 if (rv == SECSuccess) {
527 net_error = server_cert_error_; 611 DCHECK(server_cert_error_ == net::OK);
612
613 InvalidateSessionIfBadCertificate();
614
528 // there's a callback for this, too 615 // there's a callback for this, too
529 completed_handshake_ = true; 616 completed_handshake_ = true;
530 // Done! 617 // Done!
531 } else { 618 } else {
532 PRErrorCode prerr = PR_GetError(); 619 PRErrorCode prerr = PR_GetError();
533 net_error = NetErrorFromNSPRError(prerr); 620 net_error = NetErrorFromNSPRError(prerr);
534 621
535 // If not done, stay in this state 622 // If not done, stay in this state
536 if (net_error == ERR_IO_PENDING) { 623 if (net_error == ERR_IO_PENDING) {
537 GotoState(STATE_HANDSHAKE_READ); 624 GotoState(STATE_HANDSHAKE_READ);
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
579 if (prerr == PR_WOULD_BLOCK_ERROR) { 666 if (prerr == PR_WOULD_BLOCK_ERROR) {
580 GotoState(STATE_PAYLOAD_WRITE); 667 GotoState(STATE_PAYLOAD_WRITE);
581 return ERR_IO_PENDING; 668 return ERR_IO_PENDING;
582 } 669 }
583 user_buf_ = NULL; 670 user_buf_ = NULL;
584 LeaveFunction(""); 671 LeaveFunction("");
585 return NetErrorFromNSPRError(prerr); 672 return NetErrorFromNSPRError(prerr);
586 } 673 }
587 674
588 } // namespace net 675 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698