OLD | NEW |
(Empty) | |
| 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> |
| 9 // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 |
| 10 // until NSS 3.12.2 comes out and we update to it. |
| 11 #define Lock FOO_NSS_Lock |
| 12 #include <ssl.h> |
| 13 #include <pk11pub.h> |
| 14 #undef Lock |
| 15 |
| 16 #include "base/logging.h" |
| 17 #include "base/nss_init.h" |
| 18 #include "base/string_util.h" |
| 19 #include "net/base/net_errors.h" |
| 20 #include "net/base/ssl_info.h" |
| 21 |
| 22 static const int kRecvBufferSize = 4096; |
| 23 |
| 24 /* |
| 25 * nss calls this if an incoming certificate is invalid. |
| 26 * TODO(port): expose to app via GetSSLInfo so it can put up |
| 27 * the appropriate GUI and retry with override if desired |
| 28 */ |
| 29 static SECStatus |
| 30 ownBadCertHandler(void * arg, PRFileDesc * socket) |
| 31 { |
| 32 PRErrorCode err = PR_GetError(); |
| 33 LOG(ERROR) << "server certificate is invalid; NSS error code " << err; |
| 34 // Return SECSuccess to override the problem, SECFailure to let the original
function fail |
| 35 return SECSuccess; /* override, say it's OK. */ |
| 36 } |
| 37 |
| 38 |
| 39 namespace net { |
| 40 |
| 41 bool SSLClientSocketNSS::nss_options_initialized_ = false; |
| 42 |
| 43 SSLClientSocketNSS::SSLClientSocketNSS(ClientSocket* transport_socket, |
| 44 const std::string& hostname, |
| 45 const SSLConfig& ssl_config) |
| 46 : |
| 47 buffer_send_callback_(this, &SSLClientSocketNSS::BufferSendComplete), |
| 48 buffer_recv_callback_(this, &SSLClientSocketNSS::BufferRecvComplete), |
| 49 transport_send_busy_(false), |
| 50 transport_recv_busy_(false), |
| 51 io_callback_(this, &SSLClientSocketNSS::OnIOComplete), |
| 52 transport_(transport_socket), |
| 53 hostname_(hostname), |
| 54 ssl_config_(ssl_config), |
| 55 user_callback_(NULL), |
| 56 user_buf_(NULL), |
| 57 user_buf_len_(0), |
| 58 completed_handshake_(false), |
| 59 next_state_(STATE_NONE), |
| 60 nss_fd_(NULL), |
| 61 nss_bufs_(NULL) { |
| 62 } |
| 63 |
| 64 SSLClientSocketNSS::~SSLClientSocketNSS() { |
| 65 Disconnect(); |
| 66 } |
| 67 |
| 68 int SSLClientSocketNSS::Init() { |
| 69 // Call NSS_NoDB_Init() in a threadsafe way. |
| 70 base::EnsureNSSInit(); |
| 71 |
| 72 return OK; |
| 73 } |
| 74 |
| 75 int SSLClientSocketNSS::Connect(CompletionCallback* callback) { |
| 76 DCHECK(transport_.get()); |
| 77 DCHECK(next_state_ == STATE_NONE); |
| 78 DCHECK(!user_callback_); |
| 79 |
| 80 next_state_ = STATE_CONNECT; |
| 81 int rv = DoLoop(OK); |
| 82 if (rv == ERR_IO_PENDING) |
| 83 user_callback_ = callback; |
| 84 return rv; |
| 85 } |
| 86 |
| 87 int SSLClientSocketNSS::ReconnectIgnoringLastError(CompletionCallback* callback)
{ |
| 88 // TODO(darin): implement me! |
| 89 return ERR_FAILED; |
| 90 } |
| 91 |
| 92 void SSLClientSocketNSS::Disconnect() { |
| 93 // TODO(wtc): Send SSL close_notify alert. |
| 94 if (nss_fd_ != NULL) { |
| 95 PR_Close(nss_fd_); |
| 96 nss_fd_ = NULL; |
| 97 } |
| 98 completed_handshake_ = false; |
| 99 transport_->Disconnect(); |
| 100 } |
| 101 |
| 102 bool SSLClientSocketNSS::IsConnected() const { |
| 103 return completed_handshake_ && transport_->IsConnected(); |
| 104 } |
| 105 |
| 106 int SSLClientSocketNSS::Read(char* buf, int buf_len, |
| 107 CompletionCallback* callback) { |
| 108 DCHECK(completed_handshake_); |
| 109 DCHECK(next_state_ == STATE_NONE); |
| 110 DCHECK(!user_callback_); |
| 111 |
| 112 user_buf_ = buf; |
| 113 user_buf_len_ = buf_len; |
| 114 |
| 115 next_state_ = STATE_PAYLOAD_READ; |
| 116 int rv = DoLoop(OK); |
| 117 if (rv == ERR_IO_PENDING) |
| 118 user_callback_ = callback; |
| 119 return rv; |
| 120 } |
| 121 |
| 122 int SSLClientSocketNSS::Write(const char* buf, int buf_len, |
| 123 CompletionCallback* callback) { |
| 124 DCHECK(completed_handshake_); |
| 125 DCHECK(next_state_ == STATE_NONE); |
| 126 DCHECK(!user_callback_); |
| 127 |
| 128 user_buf_ = const_cast<char*>(buf); |
| 129 user_buf_len_ = buf_len; |
| 130 |
| 131 next_state_ = STATE_PAYLOAD_WRITE; |
| 132 int rv = DoLoop(OK); |
| 133 if (rv == ERR_IO_PENDING) |
| 134 user_callback_ = callback; |
| 135 return rv; |
| 136 } |
| 137 |
| 138 void SSLClientSocketNSS::GetSSLInfo(SSLInfo* ssl_info) { |
| 139 // TODO(port): implement! |
| 140 ssl_info->Reset(); |
| 141 } |
| 142 |
| 143 void SSLClientSocketNSS::DoCallback(int rv) { |
| 144 DCHECK(rv != ERR_IO_PENDING); |
| 145 DCHECK(user_callback_); |
| 146 |
| 147 // since Run may result in Read being called, clear user_callback_ up front. |
| 148 CompletionCallback* c = user_callback_; |
| 149 user_callback_ = NULL; |
| 150 c->Run(rv); |
| 151 } |
| 152 |
| 153 void SSLClientSocketNSS::OnIOComplete(int result) { |
| 154 int rv = DoLoop(result); |
| 155 if (rv != ERR_IO_PENDING) |
| 156 DoCallback(rv); |
| 157 } |
| 158 |
| 159 // Map a Chromium net error code to an NSS error code |
| 160 // See _MD_unix_map_default_error in the NSS source |
| 161 // tree for inspiration. |
| 162 static PRErrorCode MapErrorToNSS(int result) { |
| 163 if (result >=0) |
| 164 return result; |
| 165 // TODO(port): add real table |
| 166 LOG(ERROR) << "MapErrorToNSS " << result; |
| 167 return PR_UNKNOWN_ERROR; |
| 168 } |
| 169 |
| 170 /* |
| 171 * Do network I/O between the given buffer and the given socket. |
| 172 * Return 0 for EOF, |
| 173 * > 0 for bytes transferred immediately, |
| 174 * < 0 for error (or the non-error ERR_IO_PENDING). |
| 175 */ |
| 176 int SSLClientSocketNSS::BufferSend(void) { |
| 177 if (transport_send_busy_) return ERR_IO_PENDING; |
| 178 |
| 179 const char *buf; |
| 180 int nb = memio_GetWriteParams(nss_bufs_, &buf); |
| 181 |
| 182 int rv; |
| 183 if (!nb) { |
| 184 rv = OK; |
| 185 } else { |
| 186 rv = transport_->Write(buf, nb, &buffer_send_callback_); |
| 187 if (rv == ERR_IO_PENDING) |
| 188 transport_send_busy_ = true; |
| 189 else |
| 190 memio_PutWriteResult(nss_bufs_, MapErrorToNSS(rv)); |
| 191 } |
| 192 |
| 193 return rv; |
| 194 } |
| 195 |
| 196 void SSLClientSocketNSS::BufferSendComplete(int result) { |
| 197 memio_PutWriteResult(nss_bufs_, result); |
| 198 transport_send_busy_ = false; |
| 199 OnIOComplete(result); |
| 200 } |
| 201 |
| 202 |
| 203 int SSLClientSocketNSS::BufferRecv(void) { |
| 204 |
| 205 if (transport_recv_busy_) return ERR_IO_PENDING; |
| 206 |
| 207 char *buf; |
| 208 int nb = memio_GetReadParams(nss_bufs_, &buf); |
| 209 int rv; |
| 210 if (!nb) { |
| 211 // buffer too full to read into, so no I/O possible at moment |
| 212 rv = ERR_IO_PENDING; |
| 213 } else { |
| 214 rv = transport_->Read(buf, nb, &buffer_recv_callback_); |
| 215 if (rv == ERR_IO_PENDING) |
| 216 transport_recv_busy_ = true; |
| 217 else |
| 218 memio_PutReadResult(nss_bufs_, MapErrorToNSS(rv)); |
| 219 } |
| 220 |
| 221 return rv; |
| 222 } |
| 223 |
| 224 void SSLClientSocketNSS::BufferRecvComplete(int result) { |
| 225 memio_PutReadResult(nss_bufs_, result); |
| 226 transport_recv_busy_ = false; |
| 227 OnIOComplete(result); |
| 228 } |
| 229 |
| 230 |
| 231 int SSLClientSocketNSS::DoLoop(int last_io_result) { |
| 232 DCHECK(next_state_ != STATE_NONE); |
| 233 bool network_moved; |
| 234 int rv = last_io_result; |
| 235 do { |
| 236 network_moved = false; |
| 237 State state = next_state_; |
| 238 //DLOG(INFO) << "DoLoop state " << state; |
| 239 next_state_ = STATE_NONE; |
| 240 switch (state) { |
| 241 case STATE_CONNECT: |
| 242 rv = DoConnect(); |
| 243 break; |
| 244 case STATE_CONNECT_COMPLETE: |
| 245 rv = DoConnectComplete(rv); |
| 246 break; |
| 247 case STATE_HANDSHAKE_READ: |
| 248 rv = DoHandshakeRead(); |
| 249 break; |
| 250 case STATE_PAYLOAD_READ: |
| 251 rv = DoPayloadRead(); |
| 252 break; |
| 253 case STATE_PAYLOAD_WRITE: |
| 254 rv = DoPayloadWrite(); |
| 255 break; |
| 256 default: |
| 257 rv = ERR_UNEXPECTED; |
| 258 NOTREACHED() << "unexpected state"; |
| 259 break; |
| 260 } |
| 261 |
| 262 // Do the actual network I/O |
| 263 if (nss_bufs_ != NULL) { |
| 264 int nsent = BufferSend(); |
| 265 int nreceived = BufferRecv(); |
| 266 network_moved = (nsent > 0 || nreceived >= 0); |
| 267 } |
| 268 } while ((rv != ERR_IO_PENDING || network_moved) && next_state_ != STATE_NONE)
; |
| 269 return rv; |
| 270 } |
| 271 |
| 272 int SSLClientSocketNSS::DoConnect() { |
| 273 next_state_ = STATE_CONNECT_COMPLETE; |
| 274 return transport_->Connect(&io_callback_); |
| 275 } |
| 276 |
| 277 int SSLClientSocketNSS::DoConnectComplete(int result) { |
| 278 if (result < 0) |
| 279 return result; |
| 280 |
| 281 if (Init() != OK) { |
| 282 NOTREACHED() << "Couldn't initialize nss"; |
| 283 } |
| 284 |
| 285 // Transport connected, now hook it up to nss |
| 286 // TODO(port): specify rx and tx buffer sizes separately |
| 287 nss_fd_ = memio_CreateIOLayer(kRecvBufferSize); |
| 288 if (nss_fd_ == NULL) { |
| 289 return 9999; // TODO(port): real error |
| 290 } |
| 291 |
| 292 // Tell NSS who we're connected to |
| 293 PRNetAddr peername; |
| 294 socklen_t len = sizeof(PRNetAddr); |
| 295 int err = transport_->GetPeerName((struct sockaddr *)&peername, &len); |
| 296 if (err) { |
| 297 DLOG(ERROR) << "GetPeerName failed"; |
| 298 return 9999; // TODO(port): real error |
| 299 } |
| 300 memio_SetPeerName(nss_fd_, &peername); |
| 301 |
| 302 // Grab pointer to buffers |
| 303 nss_bufs_ = memio_GetSecret(nss_fd_); |
| 304 |
| 305 /* Create SSL state machine */ |
| 306 /* Push SSL onto our fake I/O socket */ |
| 307 nss_fd_ = SSL_ImportFD(NULL, nss_fd_); |
| 308 if (nss_fd_ == NULL) { |
| 309 return ERR_SSL_PROTOCOL_ERROR; // TODO(port): real error |
| 310 } |
| 311 // TODO(port): set more ssl options! Check errors! |
| 312 |
| 313 int rv; |
| 314 |
| 315 rv = SSL_OptionSet(nss_fd_, SSL_SECURITY, PR_TRUE); |
| 316 if (rv != SECSuccess) |
| 317 return ERR_UNEXPECTED; |
| 318 |
| 319 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL2, ssl_config_.ssl2_enabled); |
| 320 if (rv != SECSuccess) |
| 321 return ERR_UNEXPECTED; |
| 322 |
| 323 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.ssl3_enabled); |
| 324 if (rv != SECSuccess) |
| 325 return ERR_UNEXPECTED; |
| 326 |
| 327 rv = SSL_OptionSet(nss_fd_, SSL_ENABLE_SSL3, ssl_config_.tls1_enabled); |
| 328 if (rv != SECSuccess) |
| 329 return ERR_UNEXPECTED; |
| 330 |
| 331 rv = SSL_OptionSet(nss_fd_, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); |
| 332 if (rv != SECSuccess) |
| 333 return ERR_UNEXPECTED; |
| 334 |
| 335 rv = SSL_BadCertHook(nss_fd_, ownBadCertHandler, NULL); |
| 336 if (rv != SECSuccess) |
| 337 return ERR_UNEXPECTED; |
| 338 |
| 339 // Tell SSL the hostname we're trying to connect to. |
| 340 SSL_SetURL(nss_fd_, hostname_.c_str()); |
| 341 |
| 342 // Tell SSL we're a client; needed if not letting NSPR do socket I/O |
| 343 SSL_ResetHandshake(nss_fd_, 0); |
| 344 next_state_ = STATE_HANDSHAKE_READ; |
| 345 // Return OK so DoLoop tries handshaking |
| 346 return OK; |
| 347 } |
| 348 |
| 349 int SSLClientSocketNSS::DoHandshakeRead() { |
| 350 int rv = SSL_ForceHandshake(nss_fd_); |
| 351 if (rv == SECSuccess) { |
| 352 // there's a callback for this, too |
| 353 completed_handshake_ = true; |
| 354 // Indicate we're ready to handle I/O. Badly named? |
| 355 next_state_ = STATE_NONE; |
| 356 return OK; |
| 357 } |
| 358 PRErrorCode prerr = PR_GetError(); |
| 359 if (prerr == PR_WOULD_BLOCK_ERROR) { |
| 360 // at this point, it should have tried to send some bytes |
| 361 next_state_ = STATE_HANDSHAKE_READ; |
| 362 return ERR_IO_PENDING; |
| 363 } |
| 364 // TODO: map rv to net error code properly |
| 365 return ERR_SSL_PROTOCOL_ERROR; |
| 366 } |
| 367 |
| 368 int SSLClientSocketNSS::DoPayloadRead() { |
| 369 int rv = PR_Read(nss_fd_, user_buf_, user_buf_len_); |
| 370 if (rv >= 0) |
| 371 return rv; |
| 372 PRErrorCode prerr = PR_GetError(); |
| 373 if (prerr == PR_WOULD_BLOCK_ERROR) { |
| 374 next_state_ = STATE_PAYLOAD_READ; |
| 375 return ERR_IO_PENDING; |
| 376 } |
| 377 // TODO: map rv to net error code properly |
| 378 return ERR_SSL_PROTOCOL_ERROR; |
| 379 } |
| 380 |
| 381 int SSLClientSocketNSS::DoPayloadWrite() { |
| 382 int rv = PR_Write(nss_fd_, user_buf_, user_buf_len_); |
| 383 if (rv >= 0) |
| 384 return rv; |
| 385 PRErrorCode prerr = PR_GetError(); |
| 386 if (prerr == PR_WOULD_BLOCK_ERROR) { |
| 387 next_state_ = STATE_PAYLOAD_WRITE; |
| 388 return ERR_IO_PENDING; |
| 389 } |
| 390 // TODO: map rv to net error code properly |
| 391 return ERR_SSL_PROTOCOL_ERROR; |
| 392 } |
| 393 |
| 394 } // namespace net |
| 395 |
OLD | NEW |