[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 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 |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "net/base/ssl_client_socket_nss.h" |
| 6 | |
| 7 | #include <nspr.h> |
| 8 | #include <nss.h> |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 9 | #include <secerr.h> |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 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. |
| 12 | #define Lock FOO_NSS_Lock |
| 13 | #include <ssl.h> |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 14 | #include <sslerr.h> |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 15 | #include <pk11pub.h> |
| 16 | #undef Lock |
| 17 | |
| 18 | #include "base/logging.h" |
| 19 | #include "base/nss_init.h" |
| 20 | #include "base/string_util.h" |
| 21 | #include "net/base/net_errors.h" |
| 22 | #include "net/base/ssl_info.h" |
| 23 | |
| 24 | static const int kRecvBufferSize = 4096; |
| 25 | |
[email protected] | 9f8821ed | 2008-12-09 18:02:37 | [diff] [blame^] | 26 | namespace { |
| 27 | |
| 28 | // NSS calls this if an incoming certificate is invalid. |
| 29 | SECStatus OwnBadCertHandler(void* arg, PRFileDesc* socket) { |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 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. |
[email protected] | 9f8821ed | 2008-12-09 18:02:37 | [diff] [blame^] | 35 | LOG(WARNING) << "TODO(dkegel): return SECFailure here"; |
| 36 | return SECSuccess; |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 37 | } |
| 38 | |
[email protected] | 9f8821ed | 2008-12-09 18:02:37 | [diff] [blame^] | 39 | } // anonymous namespace |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 40 | |
| 41 | namespace net { |
| 42 | |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 43 | // State machines are easier to debug if you log state transitions. |
| 44 | // Enable these if you want to see what's going on. |
| 45 | #if 1 |
| 46 | #define EnterFunction(x) |
| 47 | #define LeaveFunction(x) |
| 48 | #define GotoState(s) next_state_ = s |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 49 | #define LogData(s, len) |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 50 | #else |
| 51 | #define EnterFunction(x) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \ |
| 52 | " enter " << x << "; next_state " << next_state_ |
| 53 | #define LeaveFunction(x) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \ |
| 54 | " leave " << x << "; next_state " << next_state_ |
| 55 | #define GotoState(s) do { LOG(INFO) << (void *)this << " " << __FUNCTION__ << \ |
| 56 | " jump to state " << s; next_state_ = s; } while (0) |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 57 | #define LogData(s, len) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \ |
| 58 | " data [" << std::string(s, len) << "]"; |
| 59 | |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 60 | #endif |
| 61 | |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 62 | namespace { |
| 63 | |
| 64 | int NetErrorFromNSPRError(PRErrorCode err) { |
| 65 | // TODO(port): fill this out as we learn what's important |
| 66 | switch (err) { |
| 67 | case PR_WOULD_BLOCK_ERROR: |
| 68 | return ERR_IO_PENDING; |
| 69 | case SSL_ERROR_NO_CYPHER_OVERLAP: |
| 70 | return ERR_SSL_VERSION_OR_CIPHER_MISMATCH; |
| 71 | case SSL_ERROR_BAD_CERT_DOMAIN: |
| 72 | return ERR_CERT_COMMON_NAME_INVALID; |
| 73 | case SEC_ERROR_EXPIRED_CERTIFICATE: |
| 74 | return ERR_CERT_DATE_INVALID; |
| 75 | case SEC_ERROR_BAD_SIGNATURE: |
| 76 | return ERR_CERT_INVALID; |
| 77 | case SSL_ERROR_REVOKED_CERT_ALERT: |
| 78 | case SEC_ERROR_REVOKED_CERTIFICATE: |
| 79 | case SEC_ERROR_REVOKED_KEY: |
| 80 | return ERR_CERT_REVOKED; |
| 81 | case SEC_ERROR_UNKNOWN_ISSUER: |
| 82 | return ERR_CERT_AUTHORITY_INVALID; |
| 83 | |
| 84 | default: { |
| 85 | if (IS_SSL_ERROR(err)) { |
| 86 | LOG(WARNING) << "Unknown SSL error " << err << |
| 87 | " mapped to net::ERR_SSL_PROTOCOL_ERROR"; |
| 88 | return ERR_SSL_PROTOCOL_ERROR; |
| 89 | } |
| 90 | if (IS_SEC_ERROR(err)) { |
| 91 | // TODO(port): Probably not the best mapping |
| 92 | LOG(WARNING) << "Unknown SEC error " << err << |
| 93 | " mapped to net::ERR_CERT_INVALID"; |
| 94 | return ERR_CERT_INVALID; |
| 95 | } |
| 96 | LOG(WARNING) << "Unknown error " << err << |
| 97 | " mapped to net::ERR_FAILED"; |
| 98 | return ERR_FAILED; |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | // Shared with the Windows code. TODO(avi): merge to a common place |
| 104 | int CertStatusFromNetError(int error) { |
| 105 | switch (error) { |
| 106 | case ERR_CERT_COMMON_NAME_INVALID: |
| 107 | return CERT_STATUS_COMMON_NAME_INVALID; |
| 108 | case ERR_CERT_DATE_INVALID: |
| 109 | return CERT_STATUS_DATE_INVALID; |
| 110 | case ERR_CERT_AUTHORITY_INVALID: |
| 111 | return CERT_STATUS_AUTHORITY_INVALID; |
| 112 | case ERR_CERT_NO_REVOCATION_MECHANISM: |
| 113 | return CERT_STATUS_NO_REVOCATION_MECHANISM; |
| 114 | case ERR_CERT_UNABLE_TO_CHECK_REVOCATION: |
| 115 | return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION; |
| 116 | case ERR_CERT_REVOKED: |
| 117 | return CERT_STATUS_REVOKED; |
| 118 | case ERR_CERT_CONTAINS_ERRORS: |
| 119 | NOTREACHED(); |
| 120 | // Falls through. |
| 121 | case ERR_CERT_INVALID: |
| 122 | return CERT_STATUS_INVALID; |
| 123 | default: |
| 124 | return 0; |
| 125 | } |
| 126 | } |
| 127 | |
| 128 | } // namespace |
| 129 | |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 130 | bool SSLClientSocketNSS::nss_options_initialized_ = false; |
| 131 | |
| 132 | SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket, |
| 133 | const std::string& hostname, |
| 134 | const SSLConfig& ssl_config) |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 135 | : |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 136 | buffer_send_callback_(this, &SSLClientSocketNSS::BufferSendComplete), |
| 137 | buffer_recv_callback_(this, &SSLClientSocketNSS::BufferRecvComplete), |
| 138 | transport_send_busy_(false), |
| 139 | transport_recv_busy_(false), |
| 140 | io_callback_(this, &SSLClientSocketNSS::OnIOComplete), |
| 141 | transport_(transport_socket), |
| 142 | hostname_(hostname), |
| 143 | ssl_config_(ssl_config), |
| 144 | user_callback_(NULL), |
| 145 | user_buf_(NULL), |
| 146 | user_buf_len_(0), |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 147 | server_cert_status_(0), |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 148 | completed_handshake_(false), |
| 149 | next_state_(STATE_NONE), |
| 150 | nss_fd_(NULL), |
| 151 | nss_bufs_(NULL) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 152 | EnterFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 153 | } |
| 154 | |
| 155 | SSLClientSocketNSS::~SSLClientSocketNSS() { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 156 | EnterFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 157 | Disconnect(); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 158 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | int SSLClientSocketNSS::Init() { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 162 | EnterFunction(""); |
| 163 | // Call NSS_NoDB_Init() in a threadsafe way. |
| 164 | base::EnsureNSSInit(); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 165 | |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 166 | LeaveFunction(""); |
| 167 | return OK; |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | int SSLClientSocketNSS::Connect(CompletionCallback* callback) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 171 | EnterFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 172 | DCHECK(transport_.get()); |
| 173 | DCHECK(next_state_ == STATE_NONE); |
| 174 | DCHECK(!user_callback_); |
| 175 | |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 176 | GotoState(STATE_CONNECT); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 177 | int rv = DoLoop(OK); |
| 178 | if (rv == ERR_IO_PENDING) |
| 179 | user_callback_ = callback; |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 180 | |
| 181 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 182 | return rv; |
| 183 | } |
| 184 | |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 185 | int SSLClientSocketNSS::ReconnectIgnoringLastError( |
| 186 | CompletionCallback* callback) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 187 | EnterFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 188 | // TODO(darin): implement me! |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 189 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 190 | return ERR_FAILED; |
| 191 | } |
| 192 | |
| 193 | void SSLClientSocketNSS::Disconnect() { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 194 | EnterFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 195 | // TODO(wtc): Send SSL close_notify alert. |
| 196 | if (nss_fd_ != NULL) { |
| 197 | PR_Close(nss_fd_); |
| 198 | nss_fd_ = NULL; |
| 199 | } |
| 200 | completed_handshake_ = false; |
| 201 | transport_->Disconnect(); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 202 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | bool SSLClientSocketNSS::IsConnected() const { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 206 | EnterFunction(""); |
| 207 | bool ret = completed_handshake_ && transport_->IsConnected(); |
| 208 | LeaveFunction(""); |
| 209 | return ret; |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 210 | } |
| 211 | |
| 212 | int SSLClientSocketNSS::Read(char* buf, int buf_len, |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 213 | CompletionCallback* callback) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 214 | EnterFunction(buf_len); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 215 | DCHECK(completed_handshake_); |
| 216 | DCHECK(next_state_ == STATE_NONE); |
| 217 | DCHECK(!user_callback_); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 218 | DCHECK(!user_buf_); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 219 | |
| 220 | user_buf_ = buf; |
| 221 | user_buf_len_ = buf_len; |
| 222 | |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 223 | GotoState(STATE_PAYLOAD_READ); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 224 | int rv = DoLoop(OK); |
| 225 | if (rv == ERR_IO_PENDING) |
| 226 | user_callback_ = callback; |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 227 | LeaveFunction(rv); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 228 | return rv; |
| 229 | } |
| 230 | |
| 231 | int SSLClientSocketNSS::Write(const char* buf, int buf_len, |
| 232 | CompletionCallback* callback) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 233 | EnterFunction(buf_len); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 234 | DCHECK(completed_handshake_); |
| 235 | DCHECK(next_state_ == STATE_NONE); |
| 236 | DCHECK(!user_callback_); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 237 | DCHECK(!user_buf_); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 238 | |
| 239 | user_buf_ = const_cast<char*>(buf); |
| 240 | user_buf_len_ = buf_len; |
| 241 | |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 242 | GotoState(STATE_PAYLOAD_WRITE); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 243 | int rv = DoLoop(OK); |
| 244 | if (rv == ERR_IO_PENDING) |
| 245 | user_callback_ = callback; |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 246 | LeaveFunction(rv); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 247 | return rv; |
| 248 | } |
| 249 | |
| 250 | void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 251 | EnterFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 252 | ssl_info->Reset(); |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 253 | SSLChannelInfo channel_info; |
| 254 | SECStatus ok = SSL_GetChannelInfo(nss_fd_, |
| 255 | &channel_info, sizeof(channel_info)); |
[email protected] | 9f8821ed | 2008-12-09 18:02:37 | [diff] [blame^] | 256 | if (ok == SECSuccess && |
| 257 | channel_info.length == sizeof(channel_info) && |
| 258 | channel_info.cipherSuite) { |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 259 | SSLCipherSuiteInfo cipher_info; |
| 260 | ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, |
| 261 | &cipher_info, sizeof(cipher_info)); |
| 262 | if (ok == SECSuccess) { |
| 263 | ssl_info->security_bits = cipher_info.effectiveKeyBits; |
| 264 | } else { |
| 265 | ssl_info->security_bits = -1; |
[email protected] | 9f8821ed | 2008-12-09 18:02:37 | [diff] [blame^] | 266 | LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError() |
| 267 | << " for cipherSuite " << channel_info.cipherSuite; |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 268 | } |
| 269 | } |
| 270 | ssl_info->cert_status = server_cert_status_; |
| 271 | // TODO(port): implement X509Certificate so we can set the cert field! |
| 272 | // CERTCertificate *nssCert = SSL_PeerCertificate(nss_fd_); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 273 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 274 | } |
| 275 | |
| 276 | void SSLClientSocketNSS::DoCallback(int rv) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 277 | EnterFunction(rv); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 278 | DCHECK(rv != ERR_IO_PENDING); |
| 279 | DCHECK(user_callback_); |
| 280 | |
| 281 | // since Run may result in Read being called, clear user_callback_ up front. |
| 282 | CompletionCallback* c = user_callback_; |
| 283 | user_callback_ = NULL; |
| 284 | c->Run(rv); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 285 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 286 | } |
| 287 | |
| 288 | void SSLClientSocketNSS::OnIOComplete(int result) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 289 | EnterFunction(result); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 290 | int rv = DoLoop(result); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 291 | if (rv != ERR_IO_PENDING && user_callback_ != NULL) |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 292 | DoCallback(rv); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 293 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 294 | } |
| 295 | |
| 296 | // Map a Chromium net error code to an NSS error code |
| 297 | // See _MD_unix_map_default_error in the NSS source |
| 298 | // tree for inspiration. |
| 299 | static PRErrorCode MapErrorToNSS(int result) { |
| 300 | if (result >=0) |
| 301 | return result; |
| 302 | // TODO(port): add real table |
| 303 | LOG(ERROR) << "MapErrorToNSS " << result; |
| 304 | return PR_UNKNOWN_ERROR; |
| 305 | } |
| 306 | |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 307 | /* |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 308 | * Do network I/O between the given buffer and the given socket. |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 309 | * Return 0 for EOF, |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 310 | * > 0 for bytes transferred immediately, |
| 311 | * < 0 for error (or the non-error ERR_IO_PENDING). |
| 312 | */ |
| 313 | int SSLClientSocketNSS::BufferSend(void) { |
| 314 | if (transport_send_busy_) return ERR_IO_PENDING; |
| 315 | |
| 316 | const char *buf; |
| 317 | int nb = memio_GetWriteParams(nss_bufs_, &buf); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 318 | EnterFunction(nb); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 319 | |
| 320 | int rv; |
| 321 | if (!nb) { |
| 322 | rv = OK; |
| 323 | } else { |
| 324 | rv = transport_->Write(buf, nb, &buffer_send_callback_); |
| 325 | if (rv == ERR_IO_PENDING) |
| 326 | transport_send_busy_ = true; |
| 327 | else |
| 328 | memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv)); |
| 329 | } |
| 330 | |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 331 | LeaveFunction(rv); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 332 | return rv; |
| 333 | } |
| 334 | |
| 335 | void SSLClientSocketNSS::BufferSendComplete(int result) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 336 | EnterFunction(result); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 337 | memio_PutWriteResult(nss_bufs_, result); |
| 338 | transport_send_busy_ = false; |
| 339 | OnIOComplete(result); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 340 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 341 | } |
| 342 | |
| 343 | |
| 344 | int SSLClientSocketNSS::BufferRecv(void) { |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 345 | if (transport_recv_busy_) return ERR_IO_PENDING; |
| 346 | |
| 347 | char *buf; |
| 348 | int nb = memio_GetReadParams(nss_bufs_, &buf); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 349 | EnterFunction(nb); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 350 | int rv; |
| 351 | if (!nb) { |
| 352 | // buffer too full to read into, so no I/O possible at moment |
| 353 | rv = ERR_IO_PENDING; |
| 354 | } else { |
| 355 | rv = transport_->Read(buf, nb, &buffer_recv_callback_); |
| 356 | if (rv == ERR_IO_PENDING) |
| 357 | transport_recv_busy_ = true; |
| 358 | else |
| 359 | memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv)); |
| 360 | } |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 361 | LeaveFunction(rv); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 362 | return rv; |
| 363 | } |
| 364 | |
| 365 | void SSLClientSocketNSS::BufferRecvComplete(int result) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 366 | EnterFunction(result); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 367 | memio_PutReadResult(nss_bufs_, result); |
| 368 | transport_recv_busy_ = false; |
| 369 | OnIOComplete(result); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 370 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 371 | } |
| 372 | |
| 373 | |
| 374 | int SSLClientSocketNSS::DoLoop(int last_io_result) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 375 | EnterFunction(last_io_result); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 376 | bool network_moved; |
| 377 | int rv = last_io_result; |
| 378 | do { |
| 379 | network_moved = false; |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 380 | // Default to STATE_NONE for next state. |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 381 | // (This is a quirk carried over from the windows |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 382 | // implementation. It makes reading the logs a bit harder.) |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 383 | // State handlers can and often do call GotoState just |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 384 | // to stay in the current state. |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 385 | State state = next_state_; |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 386 | GotoState(STATE_NONE); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 387 | switch (state) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 388 | case STATE_NONE: |
| 389 | // we're just pumping data between the buffer and the network |
| 390 | break; |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 391 | case STATE_CONNECT: |
| 392 | rv = DoConnect(); |
| 393 | break; |
| 394 | case STATE_CONNECT_COMPLETE: |
| 395 | rv = DoConnectComplete(rv); |
| 396 | break; |
| 397 | case STATE_HANDSHAKE_READ: |
| 398 | rv = DoHandshakeRead(); |
| 399 | break; |
| 400 | case STATE_PAYLOAD_READ: |
| 401 | rv = DoPayloadRead(); |
| 402 | break; |
| 403 | case STATE_PAYLOAD_WRITE: |
| 404 | rv = DoPayloadWrite(); |
| 405 | break; |
| 406 | default: |
| 407 | rv = ERR_UNEXPECTED; |
| 408 | NOTREACHED() << "unexpected state"; |
| 409 | break; |
| 410 | } |
| 411 | |
| 412 | // Do the actual network I/O |
| 413 | if (nss_bufs_ != NULL) { |
| 414 | int nsent = BufferSend(); |
| 415 | int nreceived = BufferRecv(); |
| 416 | network_moved = (nsent > 0 || nreceived >= 0); |
| 417 | } |
| 418 | } while ((rv != ERR_IO_PENDING || network_moved) && next_state_ != STATE_NONE); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 419 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 420 | return rv; |
| 421 | } |
| 422 | |
| 423 | int SSLClientSocketNSS::DoConnect() { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 424 | EnterFunction(""); |
| 425 | GotoState(STATE_CONNECT_COMPLETE); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 426 | return transport_->Connect(&io_callback_); |
| 427 | } |
| 428 | |
| 429 | int SSLClientSocketNSS::DoConnectComplete(int result) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 430 | EnterFunction(result); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 431 | if (result < 0) |
| 432 | return result; |
| 433 | |
| 434 | if (Init() != OK) { |
| 435 | NOTREACHED() << "Couldn't initialize nss"; |
| 436 | } |
| 437 | |
| 438 | // Transport connected, now hook it up to nss |
| 439 | // TODO(port): specify rx and tx buffer sizes separately |
| 440 | nss_fd_ = memio_CreateIOLayer(kRecvBufferSize); |
| 441 | if (nss_fd_ == NULL) { |
| 442 | return 9999; // TODO(port): real error |
| 443 | } |
| 444 | |
| 445 | // Tell NSS who we're connected to |
| 446 | PRNetAddr peername; |
| 447 | socklen_t len = sizeof(PRNetAddr); |
| 448 | int err = transport_->GetPeerName((struct sockaddr *)&peername, &len); |
| 449 | if (err) { |
| 450 | DLOG(ERROR) << "GetPeerName failed"; |
| 451 | return 9999; // TODO(port): real error |
| 452 | } |
| 453 | memio_SetPeerName(nss_fd_, &peername); |
| 454 | |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 455 | // Grab pointer to buffers |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 456 | nss_bufs_ = memio_GetSecret(nss_fd_); |
| 457 | |
| 458 | /* Create SSL state machine */ |
| 459 | /* Push SSL onto our fake I/O socket */ |
| 460 | nss_fd_ = SSL_ImportFD(NULL, nss_fd_); |
| 461 | if (nss_fd_ == NULL) { |
| 462 | return ERR_SSL_PROTOCOL_ERROR; // TODO(port): real error |
| 463 | } |
| 464 | // TODO(port): set more ssl options! Check errors! |
| 465 | |
| 466 | int rv; |
| 467 | |
| 468 | rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE); |
| 469 | if (rv != SECSuccess) |
| 470 | return ERR_UNEXPECTED; |
| 471 | |
| 472 | rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL2, ssl_config_.ssl2_enabled); |
| 473 | if (rv != SECSuccess) |
| 474 | return ERR_UNEXPECTED; |
| 475 | |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 476 | // SNI is enabled automatically if TLS is enabled -- as long as |
| 477 | // SSL_V2_COMPATIBLE_HELLO isn't. |
| 478 | // So don't do V2 compatible hellos unless we're really using SSL2, |
| 479 | // to avoid errors like |
| 480 | // "common name `mail.google.com' != requested host name `gmail.com'" |
| 481 | rv = SSL_OptionSet(nss_fd_, SSL_V2_COMPATIBLE_HELLO, |
| 482 | ssl_config_.ssl2_enabled); |
| 483 | if (rv != SECSuccess) |
| 484 | return ERR_UNEXPECTED; |
| 485 | |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 486 | rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.ssl3_enabled); |
| 487 | if (rv != SECSuccess) |
| 488 | return ERR_UNEXPECTED; |
| 489 | |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 490 | rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_TLS, ssl_config_.tls1_enabled); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 491 | if (rv != SECSuccess) |
| 492 | return ERR_UNEXPECTED; |
| 493 | |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 494 | #ifdef SSL_ENABLE_SESSION_TICKETS |
| 495 | // Support RFC 5077 |
| 496 | rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SESSION_TICKETS, PR_TRUE); |
| 497 | if (rv != SECSuccess) |
| 498 | LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS failed. Old system nss?"; |
| 499 | #else |
| 500 | #error "You need to install NSS-3.12 or later to build chromium" |
| 501 | #endif |
| 502 | |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 503 | rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); |
| 504 | if (rv != SECSuccess) |
| 505 | return ERR_UNEXPECTED; |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 506 | |
[email protected] | 9f8821ed | 2008-12-09 18:02:37 | [diff] [blame^] | 507 | rv = SSL_BadCertHook(nss_fd_, OwnBadCertHandler, NULL); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 508 | if (rv != SECSuccess) |
| 509 | return ERR_UNEXPECTED; |
| 510 | |
| 511 | // Tell SSL the hostname we're trying to connect to. |
| 512 | SSL_SetURL(nss_fd_, hostname_.c_str()); |
| 513 | |
| 514 | // Tell SSL we're a client; needed if not letting NSPR do socket I/O |
| 515 | SSL_ResetHandshake(nss_fd_, 0); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 516 | GotoState(STATE_HANDSHAKE_READ); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 517 | // Return OK so DoLoop tries handshaking |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 518 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 519 | return OK; |
| 520 | } |
| 521 | |
| 522 | int SSLClientSocketNSS::DoHandshakeRead() { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 523 | EnterFunction(""); |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 524 | int net_error; |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 525 | int rv = SSL_ForceHandshake(nss_fd_); |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 526 | |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 527 | if (rv == SECSuccess) { |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 528 | net_error = OK; |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 529 | // there's a callback for this, too |
| 530 | completed_handshake_ = true; |
| 531 | // Indicate we're ready to handle I/O. Badly named? |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 532 | GotoState(STATE_NONE); |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 533 | } else { |
| 534 | PRErrorCode prerr = PR_GetError(); |
| 535 | net_error = NetErrorFromNSPRError(prerr); |
| 536 | |
| 537 | // If not done, stay in this state |
| 538 | if (net_error == ERR_IO_PENDING) { |
| 539 | GotoState(STATE_HANDSHAKE_READ); |
| 540 | } else { |
| 541 | server_cert_status_ = CertStatusFromNetError(net_error); |
| 542 | LOG(ERROR) << "handshake failed; NSS error code " << prerr |
| 543 | << ", net_error " << net_error << ", server_cert_status " |
| 544 | << server_cert_status_; |
| 545 | } |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 546 | } |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 547 | |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 548 | LeaveFunction(""); |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 549 | return net_error; |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 550 | } |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 551 | |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 552 | int SSLClientSocketNSS::DoPayloadRead() { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 553 | EnterFunction(user_buf_len_); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 554 | int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 555 | if (rv >= 0) { |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 556 | LogData(user_buf_, rv); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 557 | user_buf_ = NULL; |
| 558 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 559 | return rv; |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 560 | } |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 561 | PRErrorCode prerr = PR_GetError(); |
| 562 | if (prerr == PR_WOULD_BLOCK_ERROR) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 563 | GotoState(STATE_PAYLOAD_READ); |
| 564 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 565 | return ERR_IO_PENDING; |
| 566 | } |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 567 | user_buf_ = NULL; |
| 568 | LeaveFunction(""); |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 569 | return NetErrorFromNSPRError(prerr); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 570 | } |
| 571 | |
| 572 | int SSLClientSocketNSS::DoPayloadWrite() { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 573 | EnterFunction(user_buf_len_); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 574 | int rv = PR_Write(nss_fd_, user_buf_, user_buf_len_); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 575 | if (rv >= 0) { |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 576 | LogData(user_buf_, rv); |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 577 | user_buf_ = NULL; |
| 578 | LeaveFunction(""); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 579 | return rv; |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 580 | } |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 581 | PRErrorCode prerr = PR_GetError(); |
| 582 | if (prerr == PR_WOULD_BLOCK_ERROR) { |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 583 | GotoState(STATE_PAYLOAD_WRITE); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 584 | return ERR_IO_PENDING; |
| 585 | } |
[email protected] | e17b4c1 | 2008-11-05 22:04:08 | [diff] [blame] | 586 | user_buf_ = NULL; |
| 587 | LeaveFunction(""); |
[email protected] | ea22458 | 2008-12-07 20:25:46 | [diff] [blame] | 588 | return NetErrorFromNSPRError(prerr); |
[email protected] | b43c97c | 2008-10-22 19:50:58 | [diff] [blame] | 589 | } |
| 590 | |
| 591 | } // namespace net |
| 592 | |