| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2008-2009 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_mac.h" | |
| 6 | |
| 7 #include "base/singleton.h" | |
| 8 #include "base/string_util.h" | |
| 9 #include "net/base/io_buffer.h" | |
| 10 #include "net/base/net_errors.h" | |
| 11 #include "net/base/ssl_info.h" | |
| 12 | |
| 13 // Welcome to Mac SSL. We've been waiting for you. | |
| 14 // | |
| 15 // The Mac SSL implementation is, like the Windows and NSS implementations, a | |
| 16 // giant state machine. This design constraint is due to the asynchronous nature | |
| 17 // of our underlying transport mechanism. We can call down to read/write on the | |
| 18 // network, but what happens is that either it completes immediately or returns | |
| 19 // saying that we'll get a callback sometime in the future. In that case, we | |
| 20 // have to return to our caller but pick up where we left off when we | |
| 21 // resume. Thus the fun. | |
| 22 // | |
| 23 // On Windows, we use Security Contexts, which are driven by us. We fetch data | |
| 24 // from the network, we call the context to decrypt the data, and so on. On the | |
| 25 // Mac, however, we provide Secure Transport with callbacks to get data from the | |
| 26 // network, and it calls us back to fetch the data from the network for | |
| 27 // it. Therefore, there are different sets of states in our respective state | |
| 28 // machines, fewer on the Mac because Secure Transport keeps a lot of its own | |
| 29 // state. The discussion about what each of the states means lives in comments | |
| 30 // in the DoLoop() function. | |
| 31 // | |
| 32 // Secure Transport is designed for use by either blocking or non-blocking | |
| 33 // network I/O. If, for example, you called SSLRead() to fetch data, Secure | |
| 34 // Transport will, unless it has some cached data, issue a read to your network | |
| 35 // callback read function to fetch it some more encrypted data. It's expecting | |
| 36 // one of two things. If your function is hooked up to a blocking source, then | |
| 37 // it'll block pending receipt of the data from the other end. That's fine, as | |
| 38 // when you return with the data, Secure Transport will do its thing. On the | |
| 39 // other hand, suppose that your socket is non-blocking and tells your function | |
| 40 // that it would block. Then you let Secure Transport know, and it'll tell the | |
| 41 // original caller that it would have blocked and that they need to call it | |
| 42 // "later." | |
| 43 // | |
| 44 // When's "later," though? We have fully-asynchronous networking, so we get a | |
| 45 // callback when our data's ready. But Secure Transport has no way for us to | |
| 46 // tell it that data has arrived, so we must re-execute the call that triggered | |
| 47 // the I/O (we rely on our state machine to do this). When we do so Secure | |
| 48 // Transport will ask once again for the data. Chances are that it'll be the | |
| 49 // same request as the previous time, but that's not actually guaranteed. But as | |
| 50 // long as we buffer what we have and keep track of where we were, it works | |
| 51 // quite well. | |
| 52 // | |
| 53 // Except for network writes. They shoot this plan straight to hell. | |
| 54 // | |
| 55 // Faking a blocking connection with an asynchronous connection (theoretically | |
| 56 // more powerful) simply doesn't work for writing. Suppose that Secure Transport | |
| 57 // requests a write of data to the network. With blocking I/O, we'd just block | |
| 58 // until the write completed, and with non-blocking I/O we'd know how many bytes | |
| 59 // we wrote before we would have blocked. But with the asynchronous I/O, the | |
| 60 // transport underneath us can tell us that it'll let us know sometime "later" | |
| 61 // whether or not things succeeded, and how many bytes were written. What do we | |
| 62 // return to Secure Transport? We can't return a byte count, but we can't return | |
| 63 // "later" as we're not guaranteed to be called in the future with the same data | |
| 64 // to write. | |
| 65 // | |
| 66 // So, like in any good relationship, we're forced to lie. Whenever Secure | |
| 67 // Transport asks for data to be written, we take it all and lie about it always | |
| 68 // being written. We spin in a loop (see SSLWriteCallback() and | |
| 69 // OnWriteComplete()) independent of the main state machine writing the data to | |
| 70 // the network, and get the data out. The main consequence of this independence | |
| 71 // from the state machine is that we require a full-duplex transport underneath | |
| 72 // us since we can't use it to keep our reading and writing | |
| 73 // straight. Fortunately, the NSS implementation also has this issue to deal | |
| 74 // with, so we share the same Libevent-based full-duplex TCP socket. | |
| 75 // | |
| 76 // A side comment on return values might be in order. Those who haven't taken | |
| 77 // the time to read the documentation (ahem, header comments) in our various | |
| 78 // files might be a bit surprised to see result values being treated as both | |
| 79 // lengths and errors. Like Shimmer, they are both. In both the case of | |
| 80 // immediate results as well as results returned in callbacks, a negative return | |
| 81 // value indicates an error, a zero return value indicates end-of-stream (for | |
| 82 // reads), and a positive return value indicates the number of bytes read or | |
| 83 // written. Thus, many functions start off with |if (result < 0) return | |
| 84 // result;|. That gets the error condition out of the way, and from that point | |
| 85 // forward the result can be treated as a length. | |
| 86 | |
| 87 namespace net { | |
| 88 | |
| 89 namespace { | |
| 90 | |
| 91 int NetErrorFromOSStatus(OSStatus status) { | |
| 92 switch (status) { | |
| 93 case errSSLWouldBlock: | |
| 94 return ERR_IO_PENDING; | |
| 95 case errSSLIllegalParam: | |
| 96 case errSSLBadCipherSuite: | |
| 97 case errSSLBadConfiguration: | |
| 98 return ERR_INVALID_ARGUMENT; | |
| 99 case errSSLClosedNoNotify: | |
| 100 return ERR_CONNECTION_RESET; | |
| 101 case errSSLConnectionRefused: | |
| 102 return ERR_CONNECTION_REFUSED; | |
| 103 case errSSLClosedAbort: | |
| 104 return ERR_CONNECTION_ABORTED; | |
| 105 case errSSLInternal: | |
| 106 case errSSLCrypto: | |
| 107 case errSSLFatalAlert: | |
| 108 case errSSLProtocol: | |
| 109 return ERR_SSL_PROTOCOL_ERROR; | |
| 110 case errSSLHostNameMismatch: | |
| 111 return ERR_CERT_COMMON_NAME_INVALID; | |
| 112 case errSSLCertExpired: | |
| 113 case errSSLCertNotYetValid: | |
| 114 return ERR_CERT_DATE_INVALID; | |
| 115 case errSSLNoRootCert: | |
| 116 case errSSLUnknownRootCert: | |
| 117 return ERR_CERT_AUTHORITY_INVALID; | |
| 118 case errSSLXCertChainInvalid: | |
| 119 case errSSLBadCert: | |
| 120 return ERR_CERT_INVALID; | |
| 121 case errSSLPeerCertRevoked: | |
| 122 return ERR_CERT_REVOKED; | |
| 123 | |
| 124 case errSSLClosedGraceful: | |
| 125 case noErr: | |
| 126 return OK; | |
| 127 | |
| 128 case errSSLBadRecordMac: | |
| 129 case errSSLBufferOverflow: | |
| 130 case errSSLDecryptionFail: | |
| 131 case errSSLModuleAttach: | |
| 132 case errSSLNegotiation: | |
| 133 case errSSLRecordOverflow: | |
| 134 case errSSLSessionNotFound: | |
| 135 default: | |
| 136 LOG(WARNING) << "Unknown error " << status << | |
| 137 " mapped to net::ERR_FAILED"; | |
| 138 return ERR_FAILED; | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 OSStatus OSStatusFromNetError(int net_error) { | |
| 143 switch (net_error) { | |
| 144 case ERR_IO_PENDING: | |
| 145 return errSSLWouldBlock; | |
| 146 case ERR_INTERNET_DISCONNECTED: | |
| 147 case ERR_TIMED_OUT: | |
| 148 case ERR_CONNECTION_ABORTED: | |
| 149 case ERR_CONNECTION_RESET: | |
| 150 case ERR_CONNECTION_REFUSED: | |
| 151 case ERR_ADDRESS_UNREACHABLE: | |
| 152 case ERR_ADDRESS_INVALID: | |
| 153 return errSSLClosedAbort; | |
| 154 case OK: | |
| 155 return noErr; | |
| 156 default: | |
| 157 LOG(WARNING) << "Unknown error " << net_error << | |
| 158 " mapped to errSSLIllegalParam"; | |
| 159 return errSSLIllegalParam; | |
| 160 } | |
| 161 } | |
| 162 | |
| 163 // Converts from a cipher suite to its key size. If the suite is marked with a | |
| 164 // **, it's not actually implemented in Secure Transport and won't be returned | |
| 165 // (but we'll code for it anyway). The reference here is | |
| 166 // http://www.opensource.apple.com/darwinsource/10.5.5/libsecurity_ssl-32463/lib
/cipherSpecs.c | |
| 167 // Seriously, though, there has to be an API for this, but I can't find one. | |
| 168 // Anybody? | |
| 169 int KeySizeOfCipherSuite(SSLCipherSuite suite) { | |
| 170 switch (suite) { | |
| 171 // SSL 2 only | |
| 172 | |
| 173 case SSL_RSA_WITH_DES_CBC_MD5: | |
| 174 return 56; | |
| 175 case SSL_RSA_WITH_3DES_EDE_CBC_MD5: | |
| 176 return 112; | |
| 177 case SSL_RSA_WITH_RC2_CBC_MD5: | |
| 178 case SSL_RSA_WITH_IDEA_CBC_MD5: // ** | |
| 179 return 128; | |
| 180 case SSL_NO_SUCH_CIPHERSUITE: // ** | |
| 181 return 0; | |
| 182 | |
| 183 // SSL 2, 3, TLS | |
| 184 | |
| 185 case SSL_NULL_WITH_NULL_NULL: | |
| 186 case SSL_RSA_WITH_NULL_MD5: | |
| 187 case SSL_RSA_WITH_NULL_SHA: // ** | |
| 188 case SSL_FORTEZZA_DMS_WITH_NULL_SHA: // ** | |
| 189 return 0; | |
| 190 case SSL_RSA_EXPORT_WITH_RC4_40_MD5: | |
| 191 case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5: | |
| 192 case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA: | |
| 193 case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA: // ** | |
| 194 case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA: // ** | |
| 195 case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA: | |
| 196 case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA: | |
| 197 case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5: | |
| 198 case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA: | |
| 199 return 40; | |
| 200 case SSL_RSA_WITH_DES_CBC_SHA: | |
| 201 case SSL_DH_DSS_WITH_DES_CBC_SHA: // ** | |
| 202 case SSL_DH_RSA_WITH_DES_CBC_SHA: // ** | |
| 203 case SSL_DHE_DSS_WITH_DES_CBC_SHA: | |
| 204 case SSL_DHE_RSA_WITH_DES_CBC_SHA: | |
| 205 case SSL_DH_anon_WITH_DES_CBC_SHA: | |
| 206 return 56; | |
| 207 case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA: // ** | |
| 208 return 80; | |
| 209 case SSL_RSA_WITH_3DES_EDE_CBC_SHA: | |
| 210 case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA: // ** | |
| 211 case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA: // ** | |
| 212 case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA: | |
| 213 case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA: | |
| 214 case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA: | |
| 215 return 112; | |
| 216 case SSL_RSA_WITH_RC4_128_MD5: | |
| 217 case SSL_RSA_WITH_RC4_128_SHA: | |
| 218 case SSL_RSA_WITH_IDEA_CBC_SHA: // ** | |
| 219 case SSL_DH_anon_WITH_RC4_128_MD5: | |
| 220 return 128; | |
| 221 | |
| 222 // TLS AES options (see RFC 3268) | |
| 223 | |
| 224 case TLS_RSA_WITH_AES_128_CBC_SHA: | |
| 225 case TLS_DH_DSS_WITH_AES_128_CBC_SHA: // ** | |
| 226 case TLS_DH_RSA_WITH_AES_128_CBC_SHA: // ** | |
| 227 case TLS_DHE_DSS_WITH_AES_128_CBC_SHA: | |
| 228 case TLS_DHE_RSA_WITH_AES_128_CBC_SHA: | |
| 229 case TLS_DH_anon_WITH_AES_128_CBC_SHA: | |
| 230 return 128; | |
| 231 case TLS_RSA_WITH_AES_256_CBC_SHA: | |
| 232 case TLS_DH_DSS_WITH_AES_256_CBC_SHA: // ** | |
| 233 case TLS_DH_RSA_WITH_AES_256_CBC_SHA: // ** | |
| 234 case TLS_DHE_DSS_WITH_AES_256_CBC_SHA: | |
| 235 case TLS_DHE_RSA_WITH_AES_256_CBC_SHA: | |
| 236 case TLS_DH_anon_WITH_AES_256_CBC_SHA: | |
| 237 return 256; | |
| 238 | |
| 239 default: | |
| 240 return -1; | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 } // namespace | |
| 245 | |
| 246 //----------------------------------------------------------------------------- | |
| 247 | |
| 248 SSLClientSocketMac::SSLClientSocketMac(ClientSocket* transport_socket, | |
| 249 const std::string& hostname, | |
| 250 const SSLConfig& ssl_config) | |
| 251 : io_callback_(this, &SSLClientSocketMac::OnIOComplete), | |
| 252 write_callback_(this, &SSLClientSocketMac::OnWriteComplete), | |
| 253 transport_(transport_socket), | |
| 254 hostname_(hostname), | |
| 255 ssl_config_(ssl_config), | |
| 256 user_callback_(NULL), | |
| 257 next_state_(STATE_NONE), | |
| 258 next_io_state_(STATE_NONE), | |
| 259 server_cert_status_(0), | |
| 260 completed_handshake_(false), | |
| 261 ssl_context_(NULL), | |
| 262 pending_send_error_(OK), | |
| 263 recv_buffer_head_slop_(0), | |
| 264 recv_buffer_tail_slop_(0) { | |
| 265 } | |
| 266 | |
| 267 SSLClientSocketMac::~SSLClientSocketMac() { | |
| 268 Disconnect(); | |
| 269 } | |
| 270 | |
| 271 int SSLClientSocketMac::Connect(CompletionCallback* callback) { | |
| 272 DCHECK(transport_.get()); | |
| 273 DCHECK(next_state_ == STATE_NONE); | |
| 274 DCHECK(!user_callback_); | |
| 275 | |
| 276 OSStatus status = noErr; | |
| 277 | |
| 278 status = SSLNewContext(false, &ssl_context_); | |
| 279 if (status) | |
| 280 return NetErrorFromOSStatus(status); | |
| 281 | |
| 282 status = SSLSetProtocolVersionEnabled(ssl_context_, | |
| 283 kSSLProtocol2, | |
| 284 ssl_config_.ssl2_enabled); | |
| 285 if (status) | |
| 286 return NetErrorFromOSStatus(status); | |
| 287 | |
| 288 status = SSLSetProtocolVersionEnabled(ssl_context_, | |
| 289 kSSLProtocol3, | |
| 290 ssl_config_.ssl3_enabled); | |
| 291 if (status) | |
| 292 return NetErrorFromOSStatus(status); | |
| 293 | |
| 294 status = SSLSetProtocolVersionEnabled(ssl_context_, | |
| 295 kTLSProtocol1, | |
| 296 ssl_config_.tls1_enabled); | |
| 297 if (status) | |
| 298 return NetErrorFromOSStatus(status); | |
| 299 | |
| 300 status = SSLSetIOFuncs(ssl_context_, SSLReadCallback, SSLWriteCallback); | |
| 301 if (status) | |
| 302 return NetErrorFromOSStatus(status); | |
| 303 | |
| 304 status = SSLSetConnection(ssl_context_, this); | |
| 305 if (status) | |
| 306 return NetErrorFromOSStatus(status); | |
| 307 | |
| 308 status = SSLSetPeerDomainName(ssl_context_, hostname_.c_str(), | |
| 309 hostname_.length()); | |
| 310 if (status) | |
| 311 return NetErrorFromOSStatus(status); | |
| 312 | |
| 313 next_state_ = STATE_HANDSHAKE; | |
| 314 int rv = DoLoop(OK); | |
| 315 if (rv == ERR_IO_PENDING) | |
| 316 user_callback_ = callback; | |
| 317 return rv; | |
| 318 } | |
| 319 | |
| 320 void SSLClientSocketMac::Disconnect() { | |
| 321 completed_handshake_ = false; | |
| 322 | |
| 323 if (ssl_context_) { | |
| 324 SSLClose(ssl_context_); | |
| 325 SSLDisposeContext(ssl_context_); | |
| 326 ssl_context_ = NULL; | |
| 327 } | |
| 328 | |
| 329 transport_->Disconnect(); | |
| 330 } | |
| 331 | |
| 332 bool SSLClientSocketMac::IsConnected() const { | |
| 333 // Ideally, we should also check if we have received the close_notify alert | |
| 334 // message from the server, and return false in that case. We're not doing | |
| 335 // that, so this function may return a false positive. Since the upper | |
| 336 // layer (HttpNetworkTransaction) needs to handle a persistent connection | |
| 337 // closed by the server when we send a request anyway, a false positive in | |
| 338 // exchange for simpler code is a good trade-off. | |
| 339 return completed_handshake_ && transport_->IsConnected(); | |
| 340 } | |
| 341 | |
| 342 bool SSLClientSocketMac::IsConnectedAndIdle() const { | |
| 343 // Unlike IsConnected, this method doesn't return a false positive. | |
| 344 // | |
| 345 // Strictly speaking, we should check if we have received the close_notify | |
| 346 // alert message from the server, and return false in that case. Although | |
| 347 // the close_notify alert message means EOF in the SSL layer, it is just | |
| 348 // bytes to the transport layer below, so transport_->IsConnectedAndIdle() | |
| 349 // returns the desired false when we receive close_notify. | |
| 350 return completed_handshake_ && transport_->IsConnectedAndIdle(); | |
| 351 } | |
| 352 | |
| 353 int SSLClientSocketMac::Read(IOBuffer* buf, int buf_len, | |
| 354 CompletionCallback* callback) { | |
| 355 DCHECK(completed_handshake_); | |
| 356 DCHECK(next_state_ == STATE_NONE); | |
| 357 DCHECK(!user_callback_); | |
| 358 DCHECK(!user_buf_); | |
| 359 | |
| 360 user_buf_ = buf; | |
| 361 user_buf_len_ = buf_len; | |
| 362 | |
| 363 next_state_ = STATE_PAYLOAD_READ; | |
| 364 int rv = DoLoop(OK); | |
| 365 if (rv == ERR_IO_PENDING) { | |
| 366 user_callback_ = callback; | |
| 367 } else { | |
| 368 user_buf_ = NULL; | |
| 369 } | |
| 370 return rv; | |
| 371 } | |
| 372 | |
| 373 int SSLClientSocketMac::Write(IOBuffer* buf, int buf_len, | |
| 374 CompletionCallback* callback) { | |
| 375 DCHECK(completed_handshake_); | |
| 376 DCHECK(next_state_ == STATE_NONE); | |
| 377 DCHECK(!user_callback_); | |
| 378 DCHECK(!user_buf_); | |
| 379 | |
| 380 user_buf_ = buf; | |
| 381 user_buf_len_ = buf_len; | |
| 382 | |
| 383 next_state_ = STATE_PAYLOAD_WRITE; | |
| 384 int rv = DoLoop(OK); | |
| 385 if (rv == ERR_IO_PENDING) { | |
| 386 user_callback_ = callback; | |
| 387 } else { | |
| 388 user_buf_ = NULL; | |
| 389 } | |
| 390 return rv; | |
| 391 } | |
| 392 | |
| 393 void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) { | |
| 394 ssl_info->Reset(); | |
| 395 | |
| 396 // set cert | |
| 397 CFArrayRef certs; | |
| 398 OSStatus status = SSLCopyPeerCertificates(ssl_context_, &certs); | |
| 399 if (!status) { | |
| 400 DCHECK(CFArrayGetCount(certs) > 0); | |
| 401 | |
| 402 SecCertificateRef client_cert = | |
| 403 static_cast<SecCertificateRef>( | |
| 404 const_cast<void*>(CFArrayGetValueAtIndex(certs, 0))); | |
| 405 CFRetain(client_cert); | |
| 406 ssl_info->cert = X509Certificate::CreateFromHandle( | |
| 407 client_cert, X509Certificate::SOURCE_FROM_NETWORK); | |
| 408 CFRelease(certs); | |
| 409 } | |
| 410 | |
| 411 // update status | |
| 412 ssl_info->cert_status = server_cert_status_; | |
| 413 | |
| 414 // security info | |
| 415 SSLCipherSuite suite; | |
| 416 status = SSLGetNegotiatedCipher(ssl_context_, &suite); | |
| 417 if (!status) | |
| 418 ssl_info->security_bits = KeySizeOfCipherSuite(suite); | |
| 419 } | |
| 420 | |
| 421 void SSLClientSocketMac::GetSSLCertRequestInfo( | |
| 422 SSLCertRequestInfo* cert_request_info) { | |
| 423 // TODO(wtc): implement this. | |
| 424 } | |
| 425 | |
| 426 void SSLClientSocketMac::DoCallback(int rv) { | |
| 427 DCHECK(rv != ERR_IO_PENDING); | |
| 428 DCHECK(user_callback_); | |
| 429 | |
| 430 // since Run may result in Read being called, clear user_callback_ up front. | |
| 431 CompletionCallback* c = user_callback_; | |
| 432 user_callback_ = NULL; | |
| 433 user_buf_ = NULL; | |
| 434 c->Run(rv); | |
| 435 } | |
| 436 | |
| 437 void SSLClientSocketMac::OnIOComplete(int result) { | |
| 438 if (next_io_state_ != STATE_NONE) { | |
| 439 State next_state = next_state_; | |
| 440 next_state_ = next_io_state_; | |
| 441 next_io_state_ = STATE_NONE; | |
| 442 result = DoLoop(result); | |
| 443 next_state_ = next_state; | |
| 444 } | |
| 445 if (next_state_ != STATE_NONE) { | |
| 446 int rv = DoLoop(result); | |
| 447 if (rv != ERR_IO_PENDING) | |
| 448 DoCallback(rv); | |
| 449 } | |
| 450 } | |
| 451 | |
| 452 // This is the main loop driving the state machine. Most calls coming from the | |
| 453 // outside just set up a few variables and jump into here. | |
| 454 int SSLClientSocketMac::DoLoop(int last_io_result) { | |
| 455 DCHECK(next_state_ != STATE_NONE); | |
| 456 int rv = last_io_result; | |
| 457 do { | |
| 458 State state = next_state_; | |
| 459 next_state_ = STATE_NONE; | |
| 460 switch (state) { | |
| 461 case STATE_HANDSHAKE: | |
| 462 // Do the SSL/TLS handshake. | |
| 463 rv = DoHandshake(); | |
| 464 break; | |
| 465 case STATE_READ_COMPLETE: | |
| 466 // A read off the network is complete; do the paperwork. | |
| 467 rv = DoReadComplete(rv); | |
| 468 break; | |
| 469 case STATE_PAYLOAD_READ: | |
| 470 // Do a read of data from the network. | |
| 471 rv = DoPayloadRead(); | |
| 472 break; | |
| 473 case STATE_PAYLOAD_WRITE: | |
| 474 // Do a write of data to the network. | |
| 475 rv = DoPayloadWrite(); | |
| 476 break; | |
| 477 default: | |
| 478 rv = ERR_UNEXPECTED; | |
| 479 NOTREACHED() << "unexpected state"; | |
| 480 break; | |
| 481 } | |
| 482 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
| 483 return rv; | |
| 484 } | |
| 485 | |
| 486 int SSLClientSocketMac::DoHandshake() { | |
| 487 OSStatus status = SSLHandshake(ssl_context_); | |
| 488 | |
| 489 if (status == errSSLWouldBlock) | |
| 490 next_state_ = STATE_HANDSHAKE; | |
| 491 | |
| 492 if (status == noErr) | |
| 493 completed_handshake_ = true; | |
| 494 | |
| 495 int net_error = NetErrorFromOSStatus(status); | |
| 496 | |
| 497 // At this point we have a connection. For now, we're going to use the default | |
| 498 // certificate verification that the system does, and accept its answer for | |
| 499 // the cert status. In the future, we'll need to call SSLSetEnableCertVerify | |
| 500 // to disable cert verification and do the verification ourselves. This allows | |
| 501 // very fine-grained control over what we'll accept for certification. | |
| 502 // TODO(avi): ditto | |
| 503 | |
| 504 // TODO(wtc): for now, always check revocation. | |
| 505 server_cert_status_ = CERT_STATUS_REV_CHECKING_ENABLED; | |
| 506 if (net_error) | |
| 507 server_cert_status_ |= MapNetErrorToCertStatus(net_error); | |
| 508 | |
| 509 return net_error; | |
| 510 } | |
| 511 | |
| 512 int SSLClientSocketMac::DoReadComplete(int result) { | |
| 513 if (result < 0) { | |
| 514 read_io_buf_ = NULL; | |
| 515 return result; | |
| 516 } | |
| 517 | |
| 518 char* buffer = &recv_buffer_[recv_buffer_.size() - recv_buffer_tail_slop_]; | |
| 519 memcpy(buffer, read_io_buf_->data(), result); | |
| 520 read_io_buf_ = NULL; | |
| 521 | |
| 522 recv_buffer_tail_slop_ -= result; | |
| 523 | |
| 524 return result; | |
| 525 } | |
| 526 | |
| 527 void SSLClientSocketMac::OnWriteComplete(int result) { | |
| 528 if (result < 0) { | |
| 529 pending_send_error_ = result; | |
| 530 return; | |
| 531 } | |
| 532 | |
| 533 send_buffer_.erase(send_buffer_.begin(), | |
| 534 send_buffer_.begin() + result); | |
| 535 | |
| 536 if (!send_buffer_.empty()) | |
| 537 SSLWriteCallback(this, NULL, NULL); | |
| 538 } | |
| 539 | |
| 540 int SSLClientSocketMac::DoPayloadRead() { | |
| 541 size_t processed; | |
| 542 OSStatus status = SSLRead(ssl_context_, | |
| 543 user_buf_->data(), | |
| 544 user_buf_len_, | |
| 545 &processed); | |
| 546 | |
| 547 // There's a subtle difference here in semantics of the "would block" errors. | |
| 548 // In our code, ERR_IO_PENDING means the whole operation is async, while | |
| 549 // errSSLWouldBlock means that the stream isn't ending (and is often returned | |
| 550 // along with partial data). So even though "would block" is returned, if we | |
| 551 // have data, let's just return it. | |
| 552 | |
| 553 if (processed > 0) { | |
| 554 next_state_ = STATE_NONE; | |
| 555 return processed; | |
| 556 } | |
| 557 | |
| 558 if (status == errSSLWouldBlock) { | |
| 559 next_state_ = STATE_PAYLOAD_READ; | |
| 560 } | |
| 561 | |
| 562 return NetErrorFromOSStatus(status); | |
| 563 } | |
| 564 | |
| 565 int SSLClientSocketMac::DoPayloadWrite() { | |
| 566 size_t processed; | |
| 567 OSStatus status = SSLWrite(ssl_context_, | |
| 568 user_buf_->data(), | |
| 569 user_buf_len_, | |
| 570 &processed); | |
| 571 | |
| 572 if (processed > 0) | |
| 573 return processed; | |
| 574 | |
| 575 return NetErrorFromOSStatus(status); | |
| 576 } | |
| 577 | |
| 578 // Handling the reading from the network is one of those things that should be | |
| 579 // simpler than it is. Ideally, we'd have some kind of ring buffer. For now, a | |
| 580 // std::vector<char> will have to do. | |
| 581 // | |
| 582 // The need for a buffer at all comes from the difference between an | |
| 583 // asynchronous connection (which is what we have) and a non-blocking connection | |
| 584 // (which is what we fake for Secure Transport). When Secure Transport calls us | |
| 585 // to read data, we call our underlying transport, which will likely tell us | |
| 586 // that it'll do a callback. When that happens, we need to tell Secure Transport | |
| 587 // that we've "blocked". When the callback happens, we have a chunk of data that | |
| 588 // we need to feed to Secure Transport, but it's not interested. It'll ask for | |
| 589 // it again when we call it again, so we need to hold on to the data. | |
| 590 // | |
| 591 // Why keep our own buffer? Well, when we execute a read and the underlying | |
| 592 // transport says that it'll do a callback, it keeps the pointer to the | |
| 593 // buffer. We can't pass it the buffer that Secure Transport gave us to fill, as | |
| 594 // we can't guarantee its lifetime. | |
| 595 // | |
| 596 // The basic idea, then, is this: we have a buffer filled with the data that | |
| 597 // we've read from the network but haven't given to Secure Transport | |
| 598 // yet. Whenever we read from the network the first thing we do is ensure we | |
| 599 // have enough room in the buffer for the read. We enlarge the buffer to be big | |
| 600 // enough to hold both our existing data and the new data, and then we mark the | |
| 601 // extra space at the end as "tail slop." Slop is just space at the ends of the | |
| 602 // buffer that's going to be used for data but isn't (yet). A diagram: | |
| 603 // | |
| 604 // +--------------------------------------+--------------------------------+ | |
| 605 // | existing good data ~~~~~~~~~~~~~~~~~ | tail slop area ~~~~~~~~~~~~~~~ | | |
| 606 // +--------------------------------------+--------------------------------+ | |
| 607 // | |
| 608 // When executing a read, we pass a pointer to the beginning of the tail slop | |
| 609 // area (guaranteed to be contiguous space because it's a vector, unlike, say, a | |
| 610 // deque (sigh)) and the size of the tail slop. When we get data (either here in | |
| 611 // SSLReadCallback() or above in DoReadComplete()) we subtract the number of | |
| 612 // bytes received from the tail slop value. That moves those bytes | |
| 613 // (conceptually, not physically) from the tail slop area to the area containing | |
| 614 // real data. | |
| 615 // | |
| 616 // The idea is still pretty simple. We enlarge the tail slop, call our | |
| 617 // underlying network, get data, shrink the slop area to match, copy requested | |
| 618 // data back into our caller's buffer, and delete the data from the head of the | |
| 619 // vector. | |
| 620 // | |
| 621 // Except for a nasty little problem. Asynchronous I/O calls keep the buffer | |
| 622 // pointer. | |
| 623 // | |
| 624 // This leads to the following scenario: we have a few bytes of good data in our | |
| 625 // buffer. But our caller requests more than that. We oblige by enlarging the | |
| 626 // tail slop, and calling our underlying provider, but the provider says that | |
| 627 // it'll call us back later. So we shrug our shoulders, copy what we do have | |
| 628 // into our caller's buffer and... | |
| 629 // | |
| 630 // Wait. We can't delete the data from the head of our vector. That would | |
| 631 // invalidate the pointer that we just gave to our provider. So instead, in that | |
| 632 // case we keep track of where the good data starts by keeping a "head slop" | |
| 633 // value, which just notes what data we've already sent and that is useless to | |
| 634 // us but that we can't delete because we have I/O in flight depending on us | |
| 635 // leaving the buffer alone. | |
| 636 // | |
| 637 // I hear what you're saying. "We need to use a ring buffer!" You write it, | |
| 638 // then, and I'll use it. Here are the features it needs. First, it needs to be | |
| 639 // able to have contiguous segments of arbitrary length attached to it to create | |
| 640 // read buffers. Second, each of those segments must have a "used" length | |
| 641 // indicator, so if it was half-filled by a previous data read, but the next | |
| 642 // data read is for more than there's space left, a new segment can be created | |
| 643 // for the new read without leaving an internal gap. | |
| 644 // | |
| 645 // Get to it. | |
| 646 // | |
| 647 // (sigh) Who am I kidding? TODO(avi): write the aforementioned ring buffer | |
| 648 | |
| 649 // static | |
| 650 OSStatus SSLClientSocketMac::SSLReadCallback(SSLConnectionRef connection, | |
| 651 void* data, | |
| 652 size_t* data_length) { | |
| 653 DCHECK(data); | |
| 654 DCHECK(data_length); | |
| 655 SSLClientSocketMac* us = | |
| 656 const_cast<SSLClientSocketMac*>( | |
| 657 static_cast<const SSLClientSocketMac*>(connection)); | |
| 658 | |
| 659 // If we have I/O in flight, promise we'll get back to them and use the | |
| 660 // existing callback to do so | |
| 661 | |
| 662 if (us->next_io_state_ == STATE_READ_COMPLETE) { | |
| 663 *data_length = 0; | |
| 664 return errSSLWouldBlock; | |
| 665 } | |
| 666 | |
| 667 // Start with what's in the buffer | |
| 668 | |
| 669 size_t total_read = us->recv_buffer_.size() - us->recv_buffer_head_slop_ - | |
| 670 us->recv_buffer_tail_slop_; | |
| 671 | |
| 672 // Resize the buffer if needed | |
| 673 | |
| 674 if (us->recv_buffer_.size() - us->recv_buffer_head_slop_ < *data_length) { | |
| 675 us->recv_buffer_.resize(us->recv_buffer_head_slop_ + *data_length); | |
| 676 us->recv_buffer_tail_slop_ = *data_length - total_read; | |
| 677 } | |
| 678 | |
| 679 int rv = 1; // any old value to spin the loop below | |
| 680 while (rv > 0 && total_read < *data_length) { | |
| 681 char* buffer = &us->recv_buffer_[us->recv_buffer_head_slop_ + total_read]; | |
| 682 us->read_io_buf_ = new IOBuffer(*data_length - total_read); | |
| 683 rv = us->transport_->Read(us->read_io_buf_, | |
| 684 *data_length - total_read, | |
| 685 &us->io_callback_); | |
| 686 | |
| 687 if (rv >= 0) { | |
| 688 memcpy(buffer, us->read_io_buf_->data(), rv); | |
| 689 us->read_io_buf_ = NULL; | |
| 690 total_read += rv; | |
| 691 us->recv_buffer_tail_slop_ -= rv; | |
| 692 } | |
| 693 } | |
| 694 | |
| 695 *data_length = total_read; | |
| 696 if (total_read) { | |
| 697 memcpy(data, &us->recv_buffer_[us->recv_buffer_head_slop_], total_read); | |
| 698 if (rv == ERR_IO_PENDING) { | |
| 699 // We have I/O in flight which is going to land in our buffer. We can't | |
| 700 // shuffle things around, so we need to just fiddle with pointers. | |
| 701 us->recv_buffer_head_slop_ += total_read; | |
| 702 } else { | |
| 703 us->recv_buffer_.erase(us->recv_buffer_.begin(), | |
| 704 us->recv_buffer_.begin() + | |
| 705 total_read + | |
| 706 us->recv_buffer_head_slop_); | |
| 707 us->recv_buffer_head_slop_ = 0; | |
| 708 } | |
| 709 } | |
| 710 | |
| 711 if (rv == ERR_IO_PENDING) { | |
| 712 us->next_io_state_ = STATE_READ_COMPLETE; | |
| 713 } else { | |
| 714 us->read_io_buf_ = NULL; | |
| 715 } | |
| 716 | |
| 717 if (rv < 0) | |
| 718 return OSStatusFromNetError(rv); | |
| 719 | |
| 720 return noErr; | |
| 721 } | |
| 722 | |
| 723 // static | |
| 724 OSStatus SSLClientSocketMac::SSLWriteCallback(SSLConnectionRef connection, | |
| 725 const void* data, | |
| 726 size_t* data_length) { | |
| 727 SSLClientSocketMac* us = | |
| 728 const_cast<SSLClientSocketMac*>( | |
| 729 static_cast<const SSLClientSocketMac*>(connection)); | |
| 730 | |
| 731 if (us->pending_send_error_ != OK) { | |
| 732 OSStatus status = OSStatusFromNetError(us->pending_send_error_); | |
| 733 us->pending_send_error_ = OK; | |
| 734 return status; | |
| 735 } | |
| 736 | |
| 737 if (data) | |
| 738 us->send_buffer_.insert(us->send_buffer_.end(), | |
| 739 static_cast<const char*>(data), | |
| 740 static_cast<const char*>(data) + *data_length); | |
| 741 int rv; | |
| 742 do { | |
| 743 scoped_refptr<IOBuffer> buffer = new IOBuffer(us->send_buffer_.size()); | |
| 744 memcpy(buffer->data(), &us->send_buffer_[0], us->send_buffer_.size()); | |
| 745 rv = us->transport_->Write(buffer, | |
| 746 us->send_buffer_.size(), | |
| 747 &us->write_callback_); | |
| 748 if (rv > 0) { | |
| 749 us->send_buffer_.erase(us->send_buffer_.begin(), | |
| 750 us->send_buffer_.begin() + rv); | |
| 751 } | |
| 752 } while (rv > 0 && !us->send_buffer_.empty()); | |
| 753 | |
| 754 if (rv < 0 && rv != ERR_IO_PENDING) { | |
| 755 return OSStatusFromNetError(rv); | |
| 756 } | |
| 757 | |
| 758 // always lie to our caller | |
| 759 return noErr; | |
| 760 } | |
| 761 | |
| 762 } // namespace net | |
| OLD | NEW |