Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(432)

Side by Side Diff: net/base/ssl_client_socket_nss.cc

Issue 144009: Move socket related files from net/base to net/socket. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « net/base/ssl_client_socket_nss.h ('k') | net/base/ssl_client_socket_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « net/base/ssl_client_socket_nss.h ('k') | net/base/ssl_client_socket_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698