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