| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-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 // This file includes code GetDefaultCertNickname(), derived from | |
| 6 // nsNSSCertificate::defaultServerNickName() | |
| 7 // in mozilla/security/manager/ssl/src/nsNSSCertificate.cpp | |
| 8 // and SSLClientSocketNSS::DoVerifyCertComplete() derived from | |
| 9 // AuthCertificateCallback() in | |
| 10 // mozilla/security/manager/ssl/src/nsNSSCallbacks.cpp. | |
| 11 | |
| 12 /* ***** BEGIN LICENSE BLOCK ***** | |
| 13 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | |
| 14 * | |
| 15 * The contents of this file are subject to the Mozilla Public License Version | |
| 16 * 1.1 (the "License"); you may not use this file except in compliance with | |
| 17 * the License. You may obtain a copy of the License at | |
| 18 * http://www.mozilla.org/MPL/ | |
| 19 * | |
| 20 * Software distributed under the License is distributed on an "AS IS" basis, | |
| 21 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
| 22 * for the specific language governing rights and limitations under the | |
| 23 * License. | |
| 24 * | |
| 25 * The Original Code is the Netscape security libraries. | |
| 26 * | |
| 27 * The Initial Developer of the Original Code is | |
| 28 * Netscape Communications Corporation. | |
| 29 * Portions created by the Initial Developer are Copyright (C) 2000 | |
| 30 * the Initial Developer. All Rights Reserved. | |
| 31 * | |
| 32 * Contributor(s): | |
| 33 * Ian McGreer <mcgreer@netscape.com> | |
| 34 * Javier Delgadillo <javi@netscape.com> | |
| 35 * Kai Engert <kengert@redhat.com> | |
| 36 * | |
| 37 * Alternatively, the contents of this file may be used under the terms of | |
| 38 * either the GNU General Public License Version 2 or later (the "GPL"), or | |
| 39 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | |
| 40 * in which case the provisions of the GPL or the LGPL are applicable instead | |
| 41 * of those above. If you wish to allow use of your version of this file only | |
| 42 * under the terms of either the GPL or the LGPL, and not to allow others to | |
| 43 * use your version of this file under the terms of the MPL, indicate your | |
| 44 * decision by deleting the provisions above and replace them with the notice | |
| 45 * and other provisions required by the GPL or the LGPL. If you do not delete | |
| 46 * the provisions above, a recipient may use your version of this file under | |
| 47 * the terms of any one of the MPL, the GPL or the LGPL. | |
| 48 * | |
| 49 * ***** END LICENSE BLOCK ***** */ | |
| 50 | |
| 51 #include "net/base/ssl_client_socket_nss.h" | |
| 52 | |
| 53 #include <certdb.h> | |
| 54 #include <nspr.h> | |
| 55 #include <nss.h> | |
| 56 #include <secerr.h> | |
| 57 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 | |
| 58 // until NSS 3.12.2 comes out and we update to it. | |
| 59 #define Lock FOO_NSS_Lock | |
| 60 #include <ssl.h> | |
| 61 #include <sslerr.h> | |
| 62 #include <pk11pub.h> | |
| 63 #undef Lock | |
| 64 | |
| 65 #include "base/compiler_specific.h" | |
| 66 #include "base/logging.h" | |
| 67 #include "base/nss_init.h" | |
| 68 #include "base/string_util.h" | |
| 69 #include "net/base/io_buffer.h" | |
| 70 #include "net/base/net_errors.h" | |
| 71 #include "net/base/ssl_info.h" | |
| 72 | |
| 73 static const int kRecvBufferSize = 4096; | |
| 74 | |
| 75 namespace net { | |
| 76 | |
| 77 // State machines are easier to debug if you log state transitions. | |
| 78 // Enable these if you want to see what's going on. | |
| 79 #if 1 | |
| 80 #define EnterFunction(x) | |
| 81 #define LeaveFunction(x) | |
| 82 #define GotoState(s) next_state_ = s | |
| 83 #define LogData(s, len) | |
| 84 #else | |
| 85 #define EnterFunction(x) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \ | |
| 86 " enter " << x << "; next_state " << next_state_ | |
| 87 #define LeaveFunction(x) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \ | |
| 88 " leave " << x << "; next_state " << next_state_ | |
| 89 #define GotoState(s) do { LOG(INFO) << (void *)this << " " << __FUNCTION__ << \ | |
| 90 " jump to state " << s; next_state_ = s; } while (0) | |
| 91 #define LogData(s, len) LOG(INFO) << (void *)this << " " << __FUNCTION__ << \ | |
| 92 " data [" << std::string(s, len) << "]"; | |
| 93 | |
| 94 #endif | |
| 95 | |
| 96 namespace { | |
| 97 | |
| 98 // Gets default certificate nickname from cert. | |
| 99 // Derived from nsNSSCertificate::defaultServerNickname | |
| 100 // in mozilla/security/manager/ssl/src/nsNSSCertificate.cpp. | |
| 101 std::string GetDefaultCertNickname( | |
| 102 net::X509Certificate::OSCertHandle cert) { | |
| 103 if (cert == NULL) | |
| 104 return ""; | |
| 105 | |
| 106 char* name = CERT_GetCommonName(&cert->subject); | |
| 107 if (!name) { | |
| 108 // Certs without common names are strange, but they do exist... | |
| 109 // Let's try to use another string for the nickname | |
| 110 name = CERT_GetOrgUnitName(&cert->subject); | |
| 111 if (!name) | |
| 112 name = CERT_GetOrgName(&cert->subject); | |
| 113 if (!name) | |
| 114 name = CERT_GetLocalityName(&cert->subject); | |
| 115 if (!name) | |
| 116 name = CERT_GetStateName(&cert->subject); | |
| 117 if (!name) | |
| 118 name = CERT_GetCountryName(&cert->subject); | |
| 119 if (!name) | |
| 120 return ""; | |
| 121 } | |
| 122 int count = 1; | |
| 123 std::string nickname; | |
| 124 while (1) { | |
| 125 if (count == 1) { | |
| 126 nickname = name; | |
| 127 } else { | |
| 128 nickname = StringPrintf("%s #%d", name, count); | |
| 129 } | |
| 130 PRBool conflict = SEC_CertNicknameConflict( | |
| 131 const_cast<char*>(nickname.c_str()), &cert->derSubject, cert->dbhandle); | |
| 132 if (!conflict) | |
| 133 break; | |
| 134 count++; | |
| 135 } | |
| 136 PR_FREEIF(name); | |
| 137 return nickname; | |
| 138 } | |
| 139 | |
| 140 int NetErrorFromNSPRError(PRErrorCode err) { | |
| 141 // TODO(port): fill this out as we learn what's important | |
| 142 switch (err) { | |
| 143 case PR_WOULD_BLOCK_ERROR: | |
| 144 return ERR_IO_PENDING; | |
| 145 case SSL_ERROR_NO_CYPHER_OVERLAP: | |
| 146 return ERR_SSL_VERSION_OR_CIPHER_MISMATCH; | |
| 147 case SSL_ERROR_BAD_CERT_DOMAIN: | |
| 148 return ERR_CERT_COMMON_NAME_INVALID; | |
| 149 case SEC_ERROR_EXPIRED_CERTIFICATE: | |
| 150 return ERR_CERT_DATE_INVALID; | |
| 151 case SEC_ERROR_BAD_SIGNATURE: | |
| 152 return ERR_CERT_INVALID; | |
| 153 case SSL_ERROR_REVOKED_CERT_ALERT: | |
| 154 case SEC_ERROR_REVOKED_CERTIFICATE: | |
| 155 case SEC_ERROR_REVOKED_KEY: | |
| 156 return ERR_CERT_REVOKED; | |
| 157 case SEC_ERROR_CA_CERT_INVALID: | |
| 158 case SEC_ERROR_UNKNOWN_ISSUER: | |
| 159 case SEC_ERROR_UNTRUSTED_CERT: | |
| 160 case SEC_ERROR_UNTRUSTED_ISSUER: | |
| 161 return ERR_CERT_AUTHORITY_INVALID; | |
| 162 | |
| 163 default: { | |
| 164 if (IS_SSL_ERROR(err)) { | |
| 165 LOG(WARNING) << "Unknown SSL error " << err << | |
| 166 " mapped to net::ERR_SSL_PROTOCOL_ERROR"; | |
| 167 return ERR_SSL_PROTOCOL_ERROR; | |
| 168 } | |
| 169 if (IS_SEC_ERROR(err)) { | |
| 170 // TODO(port): Probably not the best mapping | |
| 171 LOG(WARNING) << "Unknown SEC error " << err << | |
| 172 " mapped to net::ERR_CERT_INVALID"; | |
| 173 return ERR_CERT_INVALID; | |
| 174 } | |
| 175 LOG(WARNING) << "Unknown error " << err << | |
| 176 " mapped to net::ERR_FAILED"; | |
| 177 return ERR_FAILED; | |
| 178 } | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 } // namespace | |
| 183 | |
| 184 bool SSLClientSocketNSS::nss_options_initialized_ = false; | |
| 185 | |
| 186 SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket, | |
| 187 const std::string& hostname, | |
| 188 const SSLConfig& ssl_config) | |
| 189 : | |
| 190 buffer_send_callback_(this, &SSLClientSocketNSS::BufferSendComplete), | |
| 191 buffer_recv_callback_(this, &SSLClientSocketNSS::BufferRecvComplete), | |
| 192 transport_send_busy_(false), | |
| 193 transport_recv_busy_(false), | |
| 194 io_callback_(this, &SSLClientSocketNSS::OnIOComplete), | |
| 195 transport_(transport_socket), | |
| 196 hostname_(hostname), | |
| 197 ssl_config_(ssl_config), | |
| 198 user_callback_(NULL), | |
| 199 user_buf_len_(0), | |
| 200 completed_handshake_(false), | |
| 201 next_state_(STATE_NONE), | |
| 202 nss_fd_(NULL), | |
| 203 nss_bufs_(NULL) { | |
| 204 EnterFunction(""); | |
| 205 } | |
| 206 | |
| 207 SSLClientSocketNSS::~SSLClientSocketNSS() { | |
| 208 EnterFunction(""); | |
| 209 Disconnect(); | |
| 210 LeaveFunction(""); | |
| 211 } | |
| 212 | |
| 213 int SSLClientSocketNSS::Init() { | |
| 214 EnterFunction(""); | |
| 215 // Call NSS_NoDB_Init() in a threadsafe way. | |
| 216 base::EnsureNSSInit(); | |
| 217 | |
| 218 LeaveFunction(""); | |
| 219 return OK; | |
| 220 } | |
| 221 | |
| 222 // As part of Connect(), the SSLClientSocketNSS object performs an SSL | |
| 223 // handshake. This requires network IO, which in turn calls | |
| 224 // BufferRecvComplete() with a non-zero byte count. This byte count eventually | |
| 225 // winds its way through the state machine and ends up being passed to the | |
| 226 // callback. For Read() and Write(), that's what we want. But for Connect(), | |
| 227 // the caller expects OK (i.e. 0) for success. | |
| 228 // | |
| 229 // The ConnectCallbackWrapper object changes the argument that gets passed | |
| 230 // to the callback function. Any positive value gets turned into OK. | |
| 231 class ConnectCallbackWrapper : | |
| 232 public CompletionCallbackImpl<ConnectCallbackWrapper> { | |
| 233 public: | |
| 234 explicit ConnectCallbackWrapper(CompletionCallback* user_callback) | |
| 235 : ALLOW_THIS_IN_INITIALIZER_LIST( | |
| 236 CompletionCallbackImpl<ConnectCallbackWrapper>(this, | |
| 237 &ConnectCallbackWrapper::ReturnValueWrapper)), | |
| 238 user_callback_(user_callback) { | |
| 239 } | |
| 240 | |
| 241 private: | |
| 242 void ReturnValueWrapper(int rv) { | |
| 243 user_callback_->Run(rv > OK ? OK : rv); | |
| 244 delete this; | |
| 245 } | |
| 246 | |
| 247 CompletionCallback* user_callback_; | |
| 248 }; | |
| 249 | |
| 250 int SSLClientSocketNSS::Connect(CompletionCallback* callback) { | |
| 251 EnterFunction(""); | |
| 252 DCHECK(transport_.get()); | |
| 253 DCHECK(next_state_ == STATE_NONE); | |
| 254 DCHECK(!user_callback_); | |
| 255 | |
| 256 if (Init() != OK) { | |
| 257 NOTREACHED() << "Couldn't initialize nss"; | |
| 258 } | |
| 259 | |
| 260 // Transport connected, now hook it up to nss | |
| 261 // TODO(port): specify rx and tx buffer sizes separately | |
| 262 nss_fd_ = memio_CreateIOLayer(kRecvBufferSize); | |
| 263 if (nss_fd_ == NULL) { | |
| 264 return 9999; // TODO(port): real error | |
| 265 } | |
| 266 | |
| 267 // Tell NSS who we're connected to | |
| 268 PRNetAddr peername; | |
| 269 socklen_t len = sizeof(PRNetAddr); | |
| 270 int err = transport_->GetPeerName((struct sockaddr *)&peername, &len); | |
| 271 if (err) { | |
| 272 DLOG(ERROR) << "GetPeerName failed"; | |
| 273 return 9999; // TODO(port): real error | |
| 274 } | |
| 275 memio_SetPeerName(nss_fd_, &peername); | |
| 276 | |
| 277 // Grab pointer to buffers | |
| 278 nss_bufs_ = memio_GetSecret(nss_fd_); | |
| 279 | |
| 280 /* Create SSL state machine */ | |
| 281 /* Push SSL onto our fake I/O socket */ | |
| 282 nss_fd_ = SSL_ImportFD(NULL, nss_fd_); | |
| 283 if (nss_fd_ == NULL) { | |
| 284 return ERR_SSL_PROTOCOL_ERROR; // TODO(port): real error | |
| 285 } | |
| 286 // TODO(port): set more ssl options! Check errors! | |
| 287 | |
| 288 int rv; | |
| 289 | |
| 290 rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE); | |
| 291 if (rv != SECSuccess) | |
| 292 return ERR_UNEXPECTED; | |
| 293 | |
| 294 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL2, ssl_config_.ssl2_enabled); | |
| 295 if (rv != SECSuccess) | |
| 296 return ERR_UNEXPECTED; | |
| 297 | |
| 298 // SNI is enabled automatically if TLS is enabled -- as long as | |
| 299 // SSL_V2_COMPATIBLE_HELLO isn't. | |
| 300 // So don't do V2 compatible hellos unless we're really using SSL2, | |
| 301 // to avoid errors like | |
| 302 // "common name `mail.google.com' != requested host name `gmail.com'" | |
| 303 rv = SSL_OptionSet(nss_fd_, SSL_V2_COMPATIBLE_HELLO, | |
| 304 ssl_config_.ssl2_enabled); | |
| 305 if (rv != SECSuccess) | |
| 306 return ERR_UNEXPECTED; | |
| 307 | |
| 308 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.ssl3_enabled); | |
| 309 if (rv != SECSuccess) | |
| 310 return ERR_UNEXPECTED; | |
| 311 | |
| 312 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_TLS, ssl_config_.tls1_enabled); | |
| 313 if (rv != SECSuccess) | |
| 314 return ERR_UNEXPECTED; | |
| 315 | |
| 316 #ifdef SSL_ENABLE_SESSION_TICKETS | |
| 317 // Support RFC 5077 | |
| 318 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SESSION_TICKETS, PR_TRUE); | |
| 319 if (rv != SECSuccess) | |
| 320 LOG(INFO) << "SSL_ENABLE_SESSION_TICKETS failed. Old system nss?"; | |
| 321 #else | |
| 322 #error "You need to install NSS-3.12 or later to build chromium" | |
| 323 #endif | |
| 324 | |
| 325 rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); | |
| 326 if (rv != SECSuccess) | |
| 327 return ERR_UNEXPECTED; | |
| 328 | |
| 329 rv = SSL_AuthCertificateHook(nss_fd_, OwnAuthCertHandler, this); | |
| 330 if (rv != SECSuccess) | |
| 331 return ERR_UNEXPECTED; | |
| 332 | |
| 333 rv = SSL_HandshakeCallback(nss_fd_, HandshakeCallback, this); | |
| 334 if (rv != SECSuccess) | |
| 335 return ERR_UNEXPECTED; | |
| 336 | |
| 337 // Tell SSL the hostname we're trying to connect to. | |
| 338 SSL_SetURL(nss_fd_, hostname_.c_str()); | |
| 339 | |
| 340 // Tell SSL we're a client; needed if not letting NSPR do socket I/O | |
| 341 SSL_ResetHandshake(nss_fd_, 0); | |
| 342 | |
| 343 GotoState(STATE_HANDSHAKE_READ); | |
| 344 rv = DoLoop(OK); | |
| 345 if (rv == ERR_IO_PENDING) | |
| 346 user_callback_ = new ConnectCallbackWrapper(callback); | |
| 347 | |
| 348 LeaveFunction(""); | |
| 349 return rv > OK ? OK : rv; | |
| 350 } | |
| 351 | |
| 352 void SSLClientSocketNSS::InvalidateSessionIfBadCertificate() { | |
| 353 if (UpdateServerCert() != NULL && | |
| 354 ssl_config_.allowed_bad_certs_.count(server_cert_)) { | |
| 355 SSL_InvalidateSession(nss_fd_); | |
| 356 } | |
| 357 } | |
| 358 | |
| 359 void SSLClientSocketNSS::Disconnect() { | |
| 360 EnterFunction(""); | |
| 361 | |
| 362 // TODO(wtc): Send SSL close_notify alert. | |
| 363 if (nss_fd_ != NULL) { | |
| 364 InvalidateSessionIfBadCertificate(); | |
| 365 PR_Close(nss_fd_); | |
| 366 nss_fd_ = NULL; | |
| 367 } | |
| 368 | |
| 369 transport_->Disconnect(); | |
| 370 | |
| 371 // Reset object state | |
| 372 transport_send_busy_ = false; | |
| 373 transport_recv_busy_ = false; | |
| 374 user_buf_ = NULL; | |
| 375 user_buf_len_ = 0; | |
| 376 server_cert_ = NULL; | |
| 377 server_cert_verify_result_.Reset(); | |
| 378 completed_handshake_ = false; | |
| 379 nss_bufs_ = NULL; | |
| 380 | |
| 381 LeaveFunction(""); | |
| 382 } | |
| 383 | |
| 384 bool SSLClientSocketNSS::IsConnected() const { | |
| 385 // Ideally, we should also check if we have received the close_notify alert | |
| 386 // message from the server, and return false in that case. We're not doing | |
| 387 // that, so this function may return a false positive. Since the upper | |
| 388 // layer (HttpNetworkTransaction) needs to handle a persistent connection | |
| 389 // closed by the server when we send a request anyway, a false positive in | |
| 390 // exchange for simpler code is a good trade-off. | |
| 391 EnterFunction(""); | |
| 392 bool ret = completed_handshake_ && transport_->IsConnected(); | |
| 393 LeaveFunction(""); | |
| 394 return ret; | |
| 395 } | |
| 396 | |
| 397 bool SSLClientSocketNSS::IsConnectedAndIdle() const { | |
| 398 // Unlike IsConnected, this method doesn't return a false positive. | |
| 399 // | |
| 400 // Strictly speaking, we should check if we have received the close_notify | |
| 401 // alert message from the server, and return false in that case. Although | |
| 402 // the close_notify alert message means EOF in the SSL layer, it is just | |
| 403 // bytes to the transport layer below, so transport_->IsConnectedAndIdle() | |
| 404 // returns the desired false when we receive close_notify. | |
| 405 EnterFunction(""); | |
| 406 bool ret = completed_handshake_ && transport_->IsConnectedAndIdle(); | |
| 407 LeaveFunction(""); | |
| 408 return ret; | |
| 409 } | |
| 410 | |
| 411 int SSLClientSocketNSS::Read(IOBuffer* buf, int buf_len, | |
| 412 CompletionCallback* callback) { | |
| 413 EnterFunction(buf_len); | |
| 414 DCHECK(completed_handshake_); | |
| 415 DCHECK(next_state_ == STATE_NONE); | |
| 416 DCHECK(!user_callback_); | |
| 417 DCHECK(!user_buf_); | |
| 418 | |
| 419 user_buf_ = buf; | |
| 420 user_buf_len_ = buf_len; | |
| 421 | |
| 422 GotoState(STATE_PAYLOAD_READ); | |
| 423 int rv = DoLoop(OK); | |
| 424 if (rv == ERR_IO_PENDING) | |
| 425 user_callback_ = callback; | |
| 426 LeaveFunction(rv); | |
| 427 return rv; | |
| 428 } | |
| 429 | |
| 430 int SSLClientSocketNSS::Write(IOBuffer* buf, int buf_len, | |
| 431 CompletionCallback* callback) { | |
| 432 EnterFunction(buf_len); | |
| 433 DCHECK(completed_handshake_); | |
| 434 DCHECK(next_state_ == STATE_NONE); | |
| 435 DCHECK(!user_callback_); | |
| 436 DCHECK(!user_buf_); | |
| 437 | |
| 438 user_buf_ = buf; | |
| 439 user_buf_len_ = buf_len; | |
| 440 | |
| 441 GotoState(STATE_PAYLOAD_WRITE); | |
| 442 int rv = DoLoop(OK); | |
| 443 if (rv == ERR_IO_PENDING) | |
| 444 user_callback_ = callback; | |
| 445 LeaveFunction(rv); | |
| 446 return rv; | |
| 447 } | |
| 448 | |
| 449 X509Certificate *SSLClientSocketNSS::UpdateServerCert() { | |
| 450 // We set the server_cert_ from OwnAuthCertHandler(), but this handler | |
| 451 // does not necessarily get called if we are continuing a cached SSL | |
| 452 // session. | |
| 453 if (server_cert_ == NULL) { | |
| 454 X509Certificate::OSCertHandle nss_cert = SSL_PeerCertificate(nss_fd_); | |
| 455 if (nss_cert) { | |
| 456 server_cert_ = X509Certificate::CreateFromHandle( | |
| 457 nss_cert, X509Certificate::SOURCE_FROM_NETWORK); | |
| 458 } | |
| 459 } | |
| 460 return server_cert_; | |
| 461 } | |
| 462 | |
| 463 void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { | |
| 464 EnterFunction(""); | |
| 465 ssl_info->Reset(); | |
| 466 if (!server_cert_) | |
| 467 return; | |
| 468 | |
| 469 SSLChannelInfo channel_info; | |
| 470 SECStatus ok = SSL_GetChannelInfo(nss_fd_, | |
| 471 &channel_info, sizeof(channel_info)); | |
| 472 if (ok == SECSuccess && | |
| 473 channel_info.length == sizeof(channel_info) && | |
| 474 channel_info.cipherSuite) { | |
| 475 SSLCipherSuiteInfo cipher_info; | |
| 476 ok = SSL_GetCipherSuiteInfo(channel_info.cipherSuite, | |
| 477 &cipher_info, sizeof(cipher_info)); | |
| 478 if (ok == SECSuccess) { | |
| 479 ssl_info->security_bits = cipher_info.effectiveKeyBits; | |
| 480 } else { | |
| 481 ssl_info->security_bits = -1; | |
| 482 LOG(DFATAL) << "SSL_GetCipherSuiteInfo returned " << PR_GetError() | |
| 483 << " for cipherSuite " << channel_info.cipherSuite; | |
| 484 } | |
| 485 UpdateServerCert(); | |
| 486 } | |
| 487 ssl_info->cert_status = server_cert_verify_result_.cert_status; | |
| 488 DCHECK(server_cert_ != NULL); | |
| 489 ssl_info->cert = server_cert_; | |
| 490 LeaveFunction(""); | |
| 491 } | |
| 492 | |
| 493 void SSLClientSocketNSS::GetSSLCertRequestInfo( | |
| 494 SSLCertRequestInfo* cert_request_info) { | |
| 495 // TODO(wtc): implement this. | |
| 496 } | |
| 497 | |
| 498 void SSLClientSocketNSS::DoCallback(int rv) { | |
| 499 EnterFunction(rv); | |
| 500 DCHECK(rv != ERR_IO_PENDING); | |
| 501 DCHECK(user_callback_); | |
| 502 | |
| 503 // since Run may result in Read being called, clear user_callback_ up front. | |
| 504 CompletionCallback* c = user_callback_; | |
| 505 user_callback_ = NULL; | |
| 506 user_buf_ = NULL; | |
| 507 c->Run(rv); | |
| 508 LeaveFunction(""); | |
| 509 } | |
| 510 | |
| 511 void SSLClientSocketNSS::OnIOComplete(int result) { | |
| 512 EnterFunction(result); | |
| 513 int rv = DoLoop(result); | |
| 514 if (rv != ERR_IO_PENDING && user_callback_ != NULL) | |
| 515 DoCallback(rv); | |
| 516 LeaveFunction(""); | |
| 517 } | |
| 518 | |
| 519 // Map a Chromium net error code to an NSS error code | |
| 520 // See _MD_unix_map_default_error in the NSS source | |
| 521 // tree for inspiration. | |
| 522 static PRErrorCode MapErrorToNSS(int result) { | |
| 523 if (result >=0) | |
| 524 return result; | |
| 525 // TODO(port): add real table | |
| 526 LOG(ERROR) << "MapErrorToNSS " << result; | |
| 527 return PR_UNKNOWN_ERROR; | |
| 528 } | |
| 529 | |
| 530 // Do network I/O between the given buffer and the given socket. | |
| 531 // Return 0 for EOF, | |
| 532 // > 0 for bytes transferred immediately, | |
| 533 // < 0 for error (or the non-error ERR_IO_PENDING). | |
| 534 int SSLClientSocketNSS::BufferSend(void) { | |
| 535 if (transport_send_busy_) return ERR_IO_PENDING; | |
| 536 | |
| 537 const char *buf; | |
| 538 int nb = memio_GetWriteParams(nss_bufs_, &buf); | |
| 539 EnterFunction(nb); | |
| 540 | |
| 541 int rv; | |
| 542 if (!nb) { | |
| 543 rv = OK; | |
| 544 } else { | |
| 545 scoped_refptr<IOBuffer> send_buffer = new IOBuffer(nb); | |
| 546 memcpy(send_buffer->data(), buf, nb); | |
| 547 rv = transport_->Write(send_buffer, nb, &buffer_send_callback_); | |
| 548 if (rv == ERR_IO_PENDING) | |
| 549 transport_send_busy_ = true; | |
| 550 else | |
| 551 memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv)); | |
| 552 } | |
| 553 | |
| 554 LeaveFunction(rv); | |
| 555 return rv; | |
| 556 } | |
| 557 | |
| 558 void SSLClientSocketNSS::BufferSendComplete(int result) { | |
| 559 EnterFunction(result); | |
| 560 memio_PutWriteResult(nss_bufs_, result); | |
| 561 transport_send_busy_ = false; | |
| 562 OnIOComplete(result); | |
| 563 LeaveFunction(""); | |
| 564 } | |
| 565 | |
| 566 | |
| 567 int SSLClientSocketNSS::BufferRecv(void) { | |
| 568 if (transport_recv_busy_) return ERR_IO_PENDING; | |
| 569 | |
| 570 char *buf; | |
| 571 int nb = memio_GetReadParams(nss_bufs_, &buf); | |
| 572 EnterFunction(nb); | |
| 573 int rv; | |
| 574 if (!nb) { | |
| 575 // buffer too full to read into, so no I/O possible at moment | |
| 576 rv = ERR_IO_PENDING; | |
| 577 } else { | |
| 578 recv_buffer_ = new IOBuffer(nb); | |
| 579 rv = transport_->Read(recv_buffer_, nb, &buffer_recv_callback_); | |
| 580 if (rv == ERR_IO_PENDING) { | |
| 581 transport_recv_busy_ = true; | |
| 582 } else { | |
| 583 if (rv > 0) | |
| 584 memcpy(buf, recv_buffer_->data(), rv); | |
| 585 memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv)); | |
| 586 recv_buffer_ = NULL; | |
| 587 } | |
| 588 } | |
| 589 LeaveFunction(rv); | |
| 590 return rv; | |
| 591 } | |
| 592 | |
| 593 void SSLClientSocketNSS::BufferRecvComplete(int result) { | |
| 594 EnterFunction(result); | |
| 595 if (result > 0) { | |
| 596 char *buf; | |
| 597 memio_GetReadParams(nss_bufs_, &buf); | |
| 598 memcpy(buf, recv_buffer_->data(), result); | |
| 599 } | |
| 600 recv_buffer_ = NULL; | |
| 601 memio_PutReadResult(nss_bufs_, result); | |
| 602 transport_recv_busy_ = false; | |
| 603 OnIOComplete(result); | |
| 604 LeaveFunction(""); | |
| 605 } | |
| 606 | |
| 607 int SSLClientSocketNSS::DoLoop(int last_io_result) { | |
| 608 EnterFunction(last_io_result); | |
| 609 bool network_moved; | |
| 610 int rv = last_io_result; | |
| 611 do { | |
| 612 network_moved = false; | |
| 613 // Default to STATE_NONE for next state. | |
| 614 // (This is a quirk carried over from the windows | |
| 615 // implementation. It makes reading the logs a bit harder.) | |
| 616 // State handlers can and often do call GotoState just | |
| 617 // to stay in the current state. | |
| 618 State state = next_state_; | |
| 619 GotoState(STATE_NONE); | |
| 620 switch (state) { | |
| 621 case STATE_NONE: | |
| 622 // we're just pumping data between the buffer and the network | |
| 623 break; | |
| 624 case STATE_HANDSHAKE_READ: | |
| 625 rv = DoHandshakeRead(); | |
| 626 break; | |
| 627 case STATE_VERIFY_CERT: | |
| 628 DCHECK(rv == OK); | |
| 629 rv = DoVerifyCert(rv); | |
| 630 break; | |
| 631 case STATE_VERIFY_CERT_COMPLETE: | |
| 632 rv = DoVerifyCertComplete(rv); | |
| 633 break; | |
| 634 case STATE_PAYLOAD_READ: | |
| 635 rv = DoPayloadRead(); | |
| 636 break; | |
| 637 case STATE_PAYLOAD_WRITE: | |
| 638 rv = DoPayloadWrite(); | |
| 639 break; | |
| 640 default: | |
| 641 rv = ERR_UNEXPECTED; | |
| 642 NOTREACHED() << "unexpected state"; | |
| 643 break; | |
| 644 } | |
| 645 | |
| 646 // Do the actual network I/O | |
| 647 if (nss_bufs_ != NULL) { | |
| 648 int nsent = BufferSend(); | |
| 649 int nreceived = BufferRecv(); | |
| 650 network_moved = (nsent > 0 || nreceived >= 0); | |
| 651 } | |
| 652 } while ((rv != ERR_IO_PENDING || network_moved) && | |
| 653 next_state_ != STATE_NONE); | |
| 654 LeaveFunction(""); | |
| 655 return rv; | |
| 656 } | |
| 657 | |
| 658 // static | |
| 659 // NSS calls this if an incoming certificate needs to be verified. | |
| 660 // Do nothing but return SECSuccess. | |
| 661 // This is called only in full handshake mode. | |
| 662 // Peer certificate is retrieved in HandshakeCallback() later, which is called | |
| 663 // in full handshake mode or in resumption handshake mode. | |
| 664 SECStatus SSLClientSocketNSS::OwnAuthCertHandler(void* arg, | |
| 665 PRFileDesc* socket, | |
| 666 PRBool checksig, | |
| 667 PRBool is_server) { | |
| 668 // Tell NSS to not verify the certificate. | |
| 669 return SECSuccess; | |
| 670 } | |
| 671 | |
| 672 // static | |
| 673 // NSS calls this when handshake is completed. | |
| 674 // After the SSL handshake is finished, use CertVerifier to verify | |
| 675 // the saved server certificate. | |
| 676 void SSLClientSocketNSS::HandshakeCallback(PRFileDesc* socket, | |
| 677 void* arg) { | |
| 678 SSLClientSocketNSS* that = reinterpret_cast<SSLClientSocketNSS*>(arg); | |
| 679 | |
| 680 that->UpdateServerCert(); | |
| 681 } | |
| 682 | |
| 683 int SSLClientSocketNSS::DoHandshakeRead() { | |
| 684 EnterFunction(""); | |
| 685 int net_error = net::OK; | |
| 686 int rv = SSL_ForceHandshake(nss_fd_); | |
| 687 | |
| 688 if (rv == SECSuccess) { | |
| 689 // SSL handshake is completed. Let's verify the certificate. | |
| 690 GotoState(STATE_VERIFY_CERT); | |
| 691 // Done! | |
| 692 } else { | |
| 693 PRErrorCode prerr = PR_GetError(); | |
| 694 | |
| 695 // If the server closed on us, it is a protocol error. | |
| 696 // Some TLS-intolerant servers do this when we request TLS. | |
| 697 if (prerr == PR_END_OF_FILE_ERROR) { | |
| 698 net_error = ERR_SSL_PROTOCOL_ERROR; | |
| 699 } else { | |
| 700 net_error = NetErrorFromNSPRError(prerr); | |
| 701 } | |
| 702 | |
| 703 // If not done, stay in this state | |
| 704 if (net_error == ERR_IO_PENDING) { | |
| 705 GotoState(STATE_HANDSHAKE_READ); | |
| 706 } else { | |
| 707 LOG(ERROR) << "handshake failed; NSS error code " << prerr | |
| 708 << ", net_error " << net_error; | |
| 709 } | |
| 710 } | |
| 711 | |
| 712 LeaveFunction(""); | |
| 713 return net_error; | |
| 714 } | |
| 715 | |
| 716 int SSLClientSocketNSS::DoVerifyCert(int result) { | |
| 717 DCHECK(server_cert_); | |
| 718 GotoState(STATE_VERIFY_CERT_COMPLETE); | |
| 719 return verifier_.Verify(server_cert_, hostname_, | |
| 720 ssl_config_.rev_checking_enabled, | |
| 721 &server_cert_verify_result_, &io_callback_); | |
| 722 } | |
| 723 | |
| 724 // Derived from AuthCertificateCallback() in | |
| 725 // mozilla/source/security/manager/ssl/src/nsNSSCallbacks.cpp. | |
| 726 int SSLClientSocketNSS::DoVerifyCertComplete(int result) { | |
| 727 if (result == OK) { | |
| 728 // Remember the intermediate CA certs if the server sends them to us. | |
| 729 CERTCertList* cert_list = CERT_GetCertChainFromCert( | |
| 730 server_cert_->os_cert_handle(), PR_Now(), certUsageSSLCA); | |
| 731 if (cert_list) { | |
| 732 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); | |
| 733 !CERT_LIST_END(node, cert_list); | |
| 734 node = CERT_LIST_NEXT(node)) { | |
| 735 if (node->cert->slot || node->cert->isRoot || node->cert->isperm || | |
| 736 node->cert == server_cert_->os_cert_handle()) { | |
| 737 // Some certs we don't want to remember are: | |
| 738 // - found on a token. | |
| 739 // - the root cert. | |
| 740 // - already stored in perm db. | |
| 741 // - the server cert itself. | |
| 742 continue; | |
| 743 } | |
| 744 | |
| 745 // We have found a CA cert that we want to remember. | |
| 746 std::string nickname(GetDefaultCertNickname(node->cert)); | |
| 747 if (!nickname.empty()) { | |
| 748 PK11SlotInfo* slot = PK11_GetInternalKeySlot(); | |
| 749 if (slot) { | |
| 750 PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, | |
| 751 const_cast<char*>(nickname.c_str()), PR_FALSE); | |
| 752 PK11_FreeSlot(slot); | |
| 753 } | |
| 754 } | |
| 755 } | |
| 756 CERT_DestroyCertList(cert_list); | |
| 757 } | |
| 758 } | |
| 759 | |
| 760 // If we have been explicitly told to accept this certificate, override the | |
| 761 // result of verifier_.Verify. | |
| 762 // Eventually, we should cache the cert verification results so that we don't | |
| 763 // need to call verifier_.Verify repeatedly. But for now we need to do this. | |
| 764 // Alternatively, we might be able to store the cert's status along with | |
| 765 // the cert in the allowed_bad_certs_ set. | |
| 766 if (IsCertificateError(result) && | |
| 767 ssl_config_.allowed_bad_certs_.count(server_cert_)) { | |
| 768 LOG(INFO) << "accepting bad SSL certificate, as user told us to"; | |
| 769 result = OK; | |
| 770 } | |
| 771 | |
| 772 completed_handshake_ = true; | |
| 773 // TODO(ukai): we may not need this call because it is now harmless to have an | |
| 774 // session with a bad cert. | |
| 775 InvalidateSessionIfBadCertificate(); | |
| 776 // Exit DoLoop and return the result to the caller to Connect. | |
| 777 DCHECK(next_state_ == STATE_NONE); | |
| 778 return result; | |
| 779 } | |
| 780 | |
| 781 int SSLClientSocketNSS::DoPayloadRead() { | |
| 782 EnterFunction(user_buf_len_); | |
| 783 int rv = PR_Read(nss_fd_, user_buf_->data(), user_buf_len_); | |
| 784 if (rv >= 0) { | |
| 785 LogData(user_buf_->data(), rv); | |
| 786 user_buf_ = NULL; | |
| 787 LeaveFunction(""); | |
| 788 return rv; | |
| 789 } | |
| 790 PRErrorCode prerr = PR_GetError(); | |
| 791 if (prerr == PR_WOULD_BLOCK_ERROR) { | |
| 792 GotoState(STATE_PAYLOAD_READ); | |
| 793 LeaveFunction(""); | |
| 794 return ERR_IO_PENDING; | |
| 795 } | |
| 796 user_buf_ = NULL; | |
| 797 LeaveFunction(""); | |
| 798 return NetErrorFromNSPRError(prerr); | |
| 799 } | |
| 800 | |
| 801 int SSLClientSocketNSS::DoPayloadWrite() { | |
| 802 EnterFunction(user_buf_len_); | |
| 803 int rv = PR_Write(nss_fd_, user_buf_->data(), user_buf_len_); | |
| 804 if (rv >= 0) { | |
| 805 LogData(user_buf_->data(), rv); | |
| 806 user_buf_ = NULL; | |
| 807 LeaveFunction(""); | |
| 808 return rv; | |
| 809 } | |
| 810 PRErrorCode prerr = PR_GetError(); | |
| 811 if (prerr == PR_WOULD_BLOCK_ERROR) { | |
| 812 GotoState(STATE_PAYLOAD_WRITE); | |
| 813 return ERR_IO_PENDING; | |
| 814 } | |
| 815 user_buf_ = NULL; | |
| 816 LeaveFunction(""); | |
| 817 return NetErrorFromNSPRError(prerr); | |
| 818 } | |
| 819 | |
| 820 } // namespace net | |
| OLD | NEW |