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; |