| 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/logging.h" | 18 #include "base/logging.h" |
| 19 #include "base/nss_init.h" | 19 #include "base/nss_init.h" |
| 20 #include "base/string_util.h" | 20 #include "base/string_util.h" |
| 21 #include "net/base/net_errors.h" | 21 #include "net/base/net_errors.h" |
| 22 #include "net/base/ssl_info.h" | 22 #include "net/base/ssl_info.h" |
| 23 | 23 |
| 24 static const int kRecvBufferSize = 4096; | 24 static const int kRecvBufferSize = 4096; |
| 25 | 25 |
| 26 namespace { | |
| 27 | |
| 28 // NSS calls this if an incoming certificate is invalid. | |
| 29 SECStatus OwnBadCertHandler(void* arg, PRFileDesc* socket) { | |
| 30 PRErrorCode err = PR_GetError(); | |
| 31 LOG(INFO) << "server certificate is invalid; NSS error code " << err; | |
| 32 // Return SECSuccess to override the problem, | |
| 33 // or SECFailure to let the original function fail | |
| 34 // Chromium wants it to fail here, and may retry it later. | |
| 35 LOG(WARNING) << "TODO(dkegel): return SECFailure here"; | |
| 36 return SECSuccess; | |
| 37 } | |
| 38 | |
| 39 } // anonymous namespace | |
| 40 | |
| 41 namespace net { | 26 namespace net { |
| 42 | 27 |
| 43 // State machines are easier to debug if you log state transitions. | 28 // State machines are easier to debug if you log state transitions. |
| 44 // Enable these if you want to see what's going on. | 29 // Enable these if you want to see what's going on. |
| 45 #if 1 | 30 #if 1 |
| 46 #define EnterFunction(x) | 31 #define EnterFunction(x) |
| 47 #define LeaveFunction(x) | 32 #define LeaveFunction(x) |
| 48 #define GotoState(s) next_state_ = s | 33 #define GotoState(s) next_state_ = s |
| 49 #define LogData(s, len) | 34 #define LogData(s, len) |
| 50 #else | 35 #else |
| (...skipping 21 matching lines...) Expand all Loading... |
| 72 return ERR_CERT_COMMON_NAME_INVALID; | 57 return ERR_CERT_COMMON_NAME_INVALID; |
| 73 case SEC_ERROR_EXPIRED_CERTIFICATE: | 58 case SEC_ERROR_EXPIRED_CERTIFICATE: |
| 74 return ERR_CERT_DATE_INVALID; | 59 return ERR_CERT_DATE_INVALID; |
| 75 case SEC_ERROR_BAD_SIGNATURE: | 60 case SEC_ERROR_BAD_SIGNATURE: |
| 76 return ERR_CERT_INVALID; | 61 return ERR_CERT_INVALID; |
| 77 case SSL_ERROR_REVOKED_CERT_ALERT: | 62 case SSL_ERROR_REVOKED_CERT_ALERT: |
| 78 case SEC_ERROR_REVOKED_CERTIFICATE: | 63 case SEC_ERROR_REVOKED_CERTIFICATE: |
| 79 case SEC_ERROR_REVOKED_KEY: | 64 case SEC_ERROR_REVOKED_KEY: |
| 80 return ERR_CERT_REVOKED; | 65 return ERR_CERT_REVOKED; |
| 81 case SEC_ERROR_UNKNOWN_ISSUER: | 66 case SEC_ERROR_UNKNOWN_ISSUER: |
| 67 case SEC_ERROR_UNTRUSTED_CERT: |
| 68 case SEC_ERROR_UNTRUSTED_ISSUER: |
| 82 return ERR_CERT_AUTHORITY_INVALID; | 69 return ERR_CERT_AUTHORITY_INVALID; |
| 83 | 70 |
| 84 default: { | 71 default: { |
| 85 if (IS_SSL_ERROR(err)) { | 72 if (IS_SSL_ERROR(err)) { |
| 86 LOG(WARNING) << "Unknown SSL error " << err << | 73 LOG(WARNING) << "Unknown SSL error " << err << |
| 87 " mapped to net::ERR_SSL_PROTOCOL_ERROR"; | 74 " mapped to net::ERR_SSL_PROTOCOL_ERROR"; |
| 88 return ERR_SSL_PROTOCOL_ERROR; | 75 return ERR_SSL_PROTOCOL_ERROR; |
| 89 } | 76 } |
| 90 if (IS_SEC_ERROR(err)) { | 77 if (IS_SEC_ERROR(err)) { |
| 91 // TODO(port): Probably not the best mapping | 78 // TODO(port): Probably not the best mapping |
| (...skipping 20 matching lines...) Expand all Loading... |
| 112 buffer_recv_callback_(this, &SSLClientSocketNSS::BufferRecvComplete), | 99 buffer_recv_callback_(this, &SSLClientSocketNSS::BufferRecvComplete), |
| 113 transport_send_busy_(false), | 100 transport_send_busy_(false), |
| 114 transport_recv_busy_(false), | 101 transport_recv_busy_(false), |
| 115 io_callback_(this, &SSLClientSocketNSS::OnIOComplete), | 102 io_callback_(this, &SSLClientSocketNSS::OnIOComplete), |
| 116 transport_(transport_socket), | 103 transport_(transport_socket), |
| 117 hostname_(hostname), | 104 hostname_(hostname), |
| 118 ssl_config_(ssl_config), | 105 ssl_config_(ssl_config), |
| 119 user_callback_(NULL), | 106 user_callback_(NULL), |
| 120 user_buf_(NULL), | 107 user_buf_(NULL), |
| 121 user_buf_len_(0), | 108 user_buf_len_(0), |
| 122 server_cert_status_(0), | 109 server_cert_error_(0), |
| 123 completed_handshake_(false), | 110 completed_handshake_(false), |
| 124 next_state_(STATE_NONE), | 111 next_state_(STATE_NONE), |
| 125 nss_fd_(NULL), | 112 nss_fd_(NULL), |
| 126 nss_bufs_(NULL) { | 113 nss_bufs_(NULL) { |
| 127 EnterFunction(""); | 114 EnterFunction(""); |
| 128 } | 115 } |
| 129 | 116 |
| 130 SSLClientSocketNSS::~SSLClientSocketNSS() { | 117 SSLClientSocketNSS::~SSLClientSocketNSS() { |
| 131 EnterFunction(""); | 118 EnterFunction(""); |
| 132 Disconnect(); | 119 Disconnect(); |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, | 222 ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, |
| 236 &cipher_info, sizeof(cipher_info)); | 223 &cipher_info, sizeof(cipher_info)); |
| 237 if (ok == SECSuccess) { | 224 if (ok == SECSuccess) { |
| 238 ssl_info->security_bits = cipher_info.effectiveKeyBits; | 225 ssl_info->security_bits = cipher_info.effectiveKeyBits; |
| 239 } else { | 226 } else { |
| 240 ssl_info->security_bits = -1; | 227 ssl_info->security_bits = -1; |
| 241 LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError() | 228 LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError() |
| 242 << " for cipherSuite " << channel_info.cipherSuite; | 229 << " for cipherSuite " << channel_info.cipherSuite; |
| 243 } | 230 } |
| 244 } | 231 } |
| 245 ssl_info->cert_status = server_cert_status_; | 232 if (server_cert_error_ != net::OK) |
| 246 // TODO(port): implement X509Certificate so we can set the cert field! | 233 ssl_info->SetCertError(server_cert_error_); |
| 247 // CERTCertificate *nssCert = SSL_PeerCertificate(nss_fd_); | 234 X509Certificate::OSCertHandle nss_cert = SSL_PeerCertificate(nss_fd_); |
| 235 if (nss_cert) |
| 236 ssl_info->cert = X509Certificate::CreateFromHandle(nss_cert, |
| 237 X509Certificate::SOURCE_FROM_NETWORK); |
| 248 LeaveFunction(""); | 238 LeaveFunction(""); |
| 249 } | 239 } |
| 250 | 240 |
| 251 void SSLClientSocketNSS::DoCallback(int rv) { | 241 void SSLClientSocketNSS::DoCallback(int rv) { |
| 252 EnterFunction(rv); | 242 EnterFunction(rv); |
| 253 DCHECK(rv != ERR_IO_PENDING); | 243 DCHECK(rv != ERR_IO_PENDING); |
| 254 DCHECK(user_callback_); | 244 DCHECK(user_callback_); |
| 255 | 245 |
| 256 // since Run may result in Read being called, clear user_callback_ up front. | 246 // since Run may result in Read being called, clear user_callback_ up front. |
| 257 CompletionCallback* c = user_callback_; | 247 CompletionCallback* c = user_callback_; |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 LeaveFunction(""); | 384 LeaveFunction(""); |
| 395 return rv; | 385 return rv; |
| 396 } | 386 } |
| 397 | 387 |
| 398 int SSLClientSocketNSS::DoConnect() { | 388 int SSLClientSocketNSS::DoConnect() { |
| 399 EnterFunction(""); | 389 EnterFunction(""); |
| 400 GotoState(STATE_CONNECT_COMPLETE); | 390 GotoState(STATE_CONNECT_COMPLETE); |
| 401 return transport_->Connect(&io_callback_); | 391 return transport_->Connect(&io_callback_); |
| 402 } | 392 } |
| 403 | 393 |
| 394 // static |
| 395 // NSS calls this if an incoming certificate is invalid. |
| 396 SECStatus SSLClientSocketNSS::OwnBadCertHandler(void* arg, PRFileDesc* socket) { |
| 397 SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); |
| 398 PRErrorCode prerr = PR_GetError(); |
| 399 that->server_cert_error_ = NetErrorFromNSPRError(prerr); |
| 400 LOG(INFO) << "server certificate is invalid; NSS error code " << prerr |
| 401 << ", net error " << that->server_cert_error_; |
| 402 // Return SECSuccess to override the problem. |
| 403 // Chromium wants it to succeed here, and may abort the connection later. |
| 404 return SECSuccess; |
| 405 } |
| 406 |
| 404 int SSLClientSocketNSS::DoConnectComplete(int result) { | 407 int SSLClientSocketNSS::DoConnectComplete(int result) { |
| 405 EnterFunction(result); | 408 EnterFunction(result); |
| 406 if (result < 0) | 409 if (result < 0) |
| 407 return result; | 410 return result; |
| 408 | 411 |
| 409 if (Init() != OK) { | 412 if (Init() != OK) { |
| 410 NOTREACHED() << "Couldn't initialize nss"; | 413 NOTREACHED() << "Couldn't initialize nss"; |
| 411 } | 414 } |
| 412 | 415 |
| 413 // Transport connected, now hook it up to nss | 416 // Transport connected, now hook it up to nss |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 472 if (rv != SECSuccess) | 475 if (rv != SECSuccess) |
| 473 LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS failed. Old system nss?"; | 476 LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS failed. Old system nss?"; |
| 474 #else | 477 #else |
| 475 #error "You need to install NSS-3.12 or later to build chromium" | 478 #error "You need to install NSS-3.12 or later to build chromium" |
| 476 #endif | 479 #endif |
| 477 | 480 |
| 478 rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); | 481 rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); |
| 479 if (rv != SECSuccess) | 482 if (rv != SECSuccess) |
| 480 return ERR_UNEXPECTED; | 483 return ERR_UNEXPECTED; |
| 481 | 484 |
| 482 rv = SSL_BadCertHook(nss_fd_, OwnBadCertHandler, NULL); | 485 rv = SSL_BadCertHook(nss_fd_, OwnBadCertHandler, this); |
| 483 if (rv != SECSuccess) | 486 if (rv != SECSuccess) |
| 484 return ERR_UNEXPECTED; | 487 return ERR_UNEXPECTED; |
| 485 | 488 |
| 486 // Tell SSL the hostname we're trying to connect to. | 489 // Tell SSL the hostname we're trying to connect to. |
| 487 SSL_SetURL(nss_fd_, hostname_.c_str()); | 490 SSL_SetURL(nss_fd_, hostname_.c_str()); |
| 488 | 491 |
| 489 // Tell SSL we're a client; needed if not letting NSPR do socket I/O | 492 // Tell SSL we're a client; needed if not letting NSPR do socket I/O |
| 490 SSL_ResetHandshake(nss_fd_, 0); | 493 SSL_ResetHandshake(nss_fd_, 0); |
| 491 GotoState(STATE_HANDSHAKE_READ); | 494 GotoState(STATE_HANDSHAKE_READ); |
| 492 // Return OK so DoLoop tries handshaking | 495 // Return OK so DoLoop tries handshaking |
| 493 LeaveFunction(""); | 496 LeaveFunction(""); |
| 494 return OK; | 497 return OK; |
| 495 } | 498 } |
| 496 | 499 |
| 497 int SSLClientSocketNSS::DoHandshakeRead() { | 500 int SSLClientSocketNSS::DoHandshakeRead() { |
| 498 EnterFunction(""); | 501 EnterFunction(""); |
| 499 int net_error; | 502 int net_error; |
| 500 int rv = SSL_ForceHandshake(nss_fd_); | 503 int rv = SSL_ForceHandshake(nss_fd_); |
| 501 | 504 |
| 502 if (rv == SECSuccess) { | 505 if (rv == SECSuccess) { |
| 503 net_error = OK; | 506 net_error = server_cert_error_; |
| 504 // there's a callback for this, too | 507 // there's a callback for this, too |
| 505 completed_handshake_ = true; | 508 completed_handshake_ = true; |
| 506 // Indicate we're ready to handle I/O. Badly named? | 509 // Done! |
| 507 GotoState(STATE_NONE); | |
| 508 } else { | 510 } else { |
| 509 PRErrorCode prerr = PR_GetError(); | 511 PRErrorCode prerr = PR_GetError(); |
| 510 net_error = NetErrorFromNSPRError(prerr); | 512 net_error = NetErrorFromNSPRError(prerr); |
| 511 | 513 |
| 512 // If not done, stay in this state | 514 // If not done, stay in this state |
| 513 if (net_error == ERR_IO_PENDING) { | 515 if (net_error == ERR_IO_PENDING) { |
| 514 GotoState(STATE_HANDSHAKE_READ); | 516 GotoState(STATE_HANDSHAKE_READ); |
| 515 } else { | 517 } else { |
| 516 server_cert_status_ = MapNetErrorToCertStatus(net_error); | 518 server_cert_error_ = net_error; |
| 517 LOG(ERROR) << "handshake failed; NSS error code " << prerr | 519 LOG(ERROR) << "handshake failed; NSS error code " << prerr |
| 518 << ", net_error " << net_error << ", server_cert_status " | 520 << ", net_error " << net_error; |
| 519 << server_cert_status_; | |
| 520 } | 521 } |
| 521 } | 522 } |
| 522 | 523 |
| 523 LeaveFunction(""); | 524 LeaveFunction(""); |
| 524 return net_error; | 525 return net_error; |
| 525 } | 526 } |
| 526 | 527 |
| 527 int SSLClientSocketNSS::DoPayloadRead() { | 528 int SSLClientSocketNSS::DoPayloadRead() { |
| 528 EnterFunction(user_buf_len_); | 529 EnterFunction(user_buf_len_); |
| 529 int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_); | 530 int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 558 GotoState(STATE_PAYLOAD_WRITE); | 559 GotoState(STATE_PAYLOAD_WRITE); |
| 559 return ERR_IO_PENDING; | 560 return ERR_IO_PENDING; |
| 560 } | 561 } |
| 561 user_buf_ = NULL; | 562 user_buf_ = NULL; |
| 562 LeaveFunction(""); | 563 LeaveFunction(""); |
| 563 return NetErrorFromNSPRError(prerr); | 564 return NetErrorFromNSPRError(prerr); |
| 564 } | 565 } |
| 565 | 566 |
| 566 } // namespace net | 567 } // namespace net |
| 567 | 568 |
| OLD | NEW |