| Index: net/base/ssl_client_socket_nss.cc
|
| ===================================================================
|
| --- net/base/ssl_client_socket_nss.cc (revision 6192)
|
| +++ net/base/ssl_client_socket_nss.cc (working copy)
|
| @@ -6,10 +6,12 @@
|
|
|
| #include <nspr.h>
|
| #include <nss.h>
|
| +#include <secerr.h>
|
| // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424
|
| // until NSS 3.12.2 comes out and we update to it.
|
| #define Lock FOO_NSS_Lock
|
| #include <ssl.h>
|
| +#include <sslerr.h>
|
| #include <pk11pub.h>
|
| #undef Lock
|
|
|
| @@ -21,18 +23,16 @@
|
|
|
| static const int kRecvBufferSize = 4096;
|
|
|
| -/*
|
| - * nss calls this if an incoming certificate is invalid.
|
| - * TODO(port): expose to app via GetSSLInfo so it can put up
|
| - * the appropriate GUI and retry with override if desired
|
| - */
|
| +// nss calls this if an incoming certificate is invalid.
|
| static SECStatus
|
| ownBadCertHandler(void * arg, PRFileDesc * socket)
|
| {
|
| PRErrorCode err = PR_GetError();
|
| - LOG(ERROR) << "server certificate is invalid; NSS error code " << err;
|
| - // Return SECSuccess to override the problem, SECFailure to let the original function fail
|
| - return SECSuccess; /* override, say it's OK. */
|
| + LOG(INFO) << "server certificate is invalid; NSS error code " << err;
|
| + // Return SECSuccess to override the problem,
|
| + // or SECFailure to let the original function fail
|
| + // Chromium wants it to fail here, and may retry it later.
|
| + return SECFailure;
|
| }
|
|
|
|
|
| @@ -44,6 +44,7 @@
|
| #define EnterFunction(x)
|
| #define LeaveFunction(x)
|
| #define GotoState(s) next_state_ = s
|
| +#define LogData(s, len)
|
| #else
|
| #define EnterFunction(x) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
|
| " enter " << x << "; next_state " << next_state_
|
| @@ -51,8 +52,79 @@
|
| " leave " << x << "; next_state " << next_state_
|
| #define GotoState(s) do { LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
|
| " jump to state " << s; next_state_ = s; } while (0)
|
| +#define LogData(s, len) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
|
| + " data [" << std::string(s, len) << "]";
|
| +
|
| #endif
|
|
|
| +namespace {
|
| +
|
| +int NetErrorFromNSPRError(PRErrorCode err) {
|
| + // TODO(port): fill this out as we learn what's important
|
| + switch (err) {
|
| + case PR_WOULD_BLOCK_ERROR:
|
| + return ERR_IO_PENDING;
|
| + case SSL_ERROR_NO_CYPHER_OVERLAP:
|
| + return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
|
| + case SSL_ERROR_BAD_CERT_DOMAIN:
|
| + return ERR_CERT_COMMON_NAME_INVALID;
|
| + case SEC_ERROR_EXPIRED_CERTIFICATE:
|
| + return ERR_CERT_DATE_INVALID;
|
| + case SEC_ERROR_BAD_SIGNATURE:
|
| + return ERR_CERT_INVALID;
|
| + case SSL_ERROR_REVOKED_CERT_ALERT:
|
| + case SEC_ERROR_REVOKED_CERTIFICATE:
|
| + case SEC_ERROR_REVOKED_KEY:
|
| + return ERR_CERT_REVOKED;
|
| + case SEC_ERROR_UNKNOWN_ISSUER:
|
| + return ERR_CERT_AUTHORITY_INVALID;
|
| +
|
| + default: {
|
| + if (IS_SSL_ERROR(err)) {
|
| + LOG(WARNING) << "Unknown SSL error " << err <<
|
| + " mapped to net::ERR_SSL_PROTOCOL_ERROR";
|
| + return ERR_SSL_PROTOCOL_ERROR;
|
| + }
|
| + if (IS_SEC_ERROR(err)) {
|
| + // TODO(port): Probably not the best mapping
|
| + LOG(WARNING) << "Unknown SEC error " << err <<
|
| + " mapped to net::ERR_CERT_INVALID";
|
| + return ERR_CERT_INVALID;
|
| + }
|
| + LOG(WARNING) << "Unknown error " << err <<
|
| + " mapped to net::ERR_FAILED";
|
| + return ERR_FAILED;
|
| + }
|
| + }
|
| +}
|
| +
|
| +// Shared with the Windows code. TODO(avi): merge to a common place
|
| +int CertStatusFromNetError(int error) {
|
| + switch (error) {
|
| + case ERR_CERT_COMMON_NAME_INVALID:
|
| + return CERT_STATUS_COMMON_NAME_INVALID;
|
| + case ERR_CERT_DATE_INVALID:
|
| + return CERT_STATUS_DATE_INVALID;
|
| + case ERR_CERT_AUTHORITY_INVALID:
|
| + return CERT_STATUS_AUTHORITY_INVALID;
|
| + case ERR_CERT_NO_REVOCATION_MECHANISM:
|
| + return CERT_STATUS_NO_REVOCATION_MECHANISM;
|
| + case ERR_CERT_UNABLE_TO_CHECK_REVOCATION:
|
| + return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
|
| + case ERR_CERT_REVOKED:
|
| + return CERT_STATUS_REVOKED;
|
| + case ERR_CERT_CONTAINS_ERRORS:
|
| + NOTREACHED();
|
| + // Falls through.
|
| + case ERR_CERT_INVALID:
|
| + return CERT_STATUS_INVALID;
|
| + default:
|
| + return 0;
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| bool SSLClientSocketNSS::nss_options_initialized_ = false;
|
|
|
| SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket,
|
| @@ -70,6 +142,7 @@
|
| user_callback_(NULL),
|
| user_buf_(NULL),
|
| user_buf_len_(0),
|
| + server_cert_status_(0),
|
| completed_handshake_(false),
|
| next_state_(STATE_NONE),
|
| nss_fd_(NULL),
|
| @@ -148,7 +221,7 @@
|
| int rv = DoLoop(OK);
|
| if (rv == ERR_IO_PENDING)
|
| user_callback_ = callback;
|
| - LeaveFunction("");
|
| + LeaveFunction(rv);
|
| return rv;
|
| }
|
|
|
| @@ -167,14 +240,30 @@
|
| int rv = DoLoop(OK);
|
| if (rv == ERR_IO_PENDING)
|
| user_callback_ = callback;
|
| - LeaveFunction("");
|
| + LeaveFunction(rv);
|
| return rv;
|
| }
|
|
|
| void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) {
|
| EnterFunction("");
|
| - // TODO(port): implement!
|
| ssl_info->Reset();
|
| + SSLChannelInfo channel_info;
|
| + SECStatus ok = SSL_GetChannelInfo(nss_fd_,
|
| + &channel_info, sizeof(channel_info));
|
| + if (ok == SECSuccess) {
|
| + SSLCipherSuiteInfo cipher_info;
|
| + ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite,
|
| + &cipher_info, sizeof(cipher_info));
|
| + if (ok == SECSuccess) {
|
| + ssl_info->security_bits = cipher_info.effectiveKeyBits;
|
| + } else {
|
| + ssl_info->security_bits = -1;
|
| + NOTREACHED();
|
| + }
|
| + }
|
| + ssl_info->cert_status = server_cert_status_;
|
| + // TODO(port): implement X509Certificate so we can set the cert field!
|
| + // CERTCertificate *nssCert = SSL_PeerCertificate(nss_fd_);
|
| LeaveFunction("");
|
| }
|
|
|
| @@ -378,14 +467,33 @@
|
| if (rv != SECSuccess)
|
| return ERR_UNEXPECTED;
|
|
|
| + // SNI is enabled automatically if TLS is enabled -- as long as
|
| + // SSL_V2_COMPATIBLE_HELLO isn't.
|
| + // So don't do V2 compatible hellos unless we're really using SSL2,
|
| + // to avoid errors like
|
| + // "common name `mail.google.com' != requested host name `gmail.com'"
|
| + rv = SSL_OptionSet(nss_fd_, SSL_V2_COMPATIBLE_HELLO,
|
| + ssl_config_.ssl2_enabled);
|
| + if (rv != SECSuccess)
|
| + return ERR_UNEXPECTED;
|
| +
|
| rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.ssl3_enabled);
|
| if (rv != SECSuccess)
|
| return ERR_UNEXPECTED;
|
|
|
| - rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.tls1_enabled);
|
| + rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_TLS, ssl_config_.tls1_enabled);
|
| if (rv != SECSuccess)
|
| return ERR_UNEXPECTED;
|
|
|
| +#ifdef SSL_ENABLE_SESSION_TICKETS
|
| + // Support RFC 5077
|
| + rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
|
| + if (rv != SECSuccess)
|
| + LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS failed. Old system nss?";
|
| +#else
|
| + #error "You need to install NSS-3.12 or later to build chromium"
|
| +#endif
|
| +
|
| rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
|
| if (rv != SECSuccess)
|
| return ERR_UNEXPECTED;
|
| @@ -407,31 +515,38 @@
|
|
|
| int SSLClientSocketNSS::DoHandshakeRead() {
|
| EnterFunction("");
|
| + int net_error;
|
| int rv = SSL_ForceHandshake(nss_fd_);
|
| +
|
| if (rv == SECSuccess) {
|
| + net_error = OK;
|
| // there's a callback for this, too
|
| completed_handshake_ = true;
|
| // Indicate we're ready to handle I/O. Badly named?
|
| GotoState(STATE_NONE);
|
| - LeaveFunction("");
|
| - return OK;
|
| + } else {
|
| + PRErrorCode prerr = PR_GetError();
|
| + net_error = NetErrorFromNSPRError(prerr);
|
| +
|
| + // If not done, stay in this state
|
| + if (net_error == ERR_IO_PENDING) {
|
| + GotoState(STATE_HANDSHAKE_READ);
|
| + } else {
|
| + server_cert_status_ = CertStatusFromNetError(net_error);
|
| + LOG(ERROR) << "handshake failed; NSS error code " << prerr
|
| + << ", net_error " << net_error << ", server_cert_status " << server_cert_status_;
|
| + }
|
| }
|
| - PRErrorCode prerr = PR_GetError();
|
| - if (prerr == PR_WOULD_BLOCK_ERROR) {
|
| - // at this point, it should have tried to send some bytes
|
| - GotoState(STATE_HANDSHAKE_READ);
|
| - LeaveFunction("");
|
| - return ERR_IO_PENDING;
|
| - }
|
| - // TODO: map rv to net error code properly
|
| +
|
| LeaveFunction("");
|
| - return ERR_SSL_PROTOCOL_ERROR;
|
| + return net_error;
|
| }
|
|
|
| int SSLClientSocketNSS::DoPayloadRead() {
|
| EnterFunction(user_buf_len_);
|
| int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_);
|
| if (rv >= 0) {
|
| + LogData(user_buf_, rv);
|
| user_buf_ = NULL;
|
| LeaveFunction("");
|
| return rv;
|
| @@ -452,6 +567,7 @@
|
| EnterFunction(user_buf_len_);
|
| int rv = PR_Write(nss_fd_, user_buf_, user_buf_len_);
|
| if (rv >= 0) {
|
| + LogData(user_buf_, rv);
|
| user_buf_ = NULL;
|
| LeaveFunction("");
|
| return rv;
|
|
|