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

Side by Side Diff: net/socket/ssl_client_socket_openssl.cc

Issue 3495005: Prototype OpenSSL client socket implementation. (Closed)
Patch Set: agl comments no. 2 Created 10 years, 2 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
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 // OpenSSL binding for SSLClientSocket. The class layout and general principle
6 // of operation is derived from SSLClientSocketNSS.
7
8 #include "net/socket/ssl_client_socket_openssl.h"
9
10 #include <openssl/ssl.h>
11 #include <openssl/err.h>
12
13 #include "net/base/net_errors.h"
14 #include "net/base/ssl_connection_status_flags.h"
15 #include "net/base/ssl_info.h"
16 #include "net/base/test_certificate_data.h" // TODO(joth): Remove!!
17
18 namespace net {
19
20 namespace {
21
22 // Enable this to see logging for state machine state transitions.
wtc 2010/09/28 18:08:35 Nit: this comment says "Enable this", but the ifde
joth 2010/09/29 11:58:54 Done. (It was left as was for consistency with NSS
23 #if 1
24 #define GotoState(s) next_handshake_state_ = s
25 #else
26 #define GotoState(s) do { LOG(INFO) << (void *)this << " " << __FUNCTION__ << \
27 " jump to state " << s; \
28 next_handshake_state_ = s; } while (0)
29 #endif
30
31 const size_t kMaxRecvBufferSize = 4096;
32
33 void MaybeLogSSLError() {
34 int error_num;
35 while ((error_num = ERR_get_error()) != 0) {
36 char buf[128]; // this buffer must be at least 120 chars long.
37 ERR_error_string_n(error_num, buf, arraysize(buf));
38 LOG(INFO) << "SSL error " << error_num << ": " << buf;
39 }
40 }
41
42 int MapOpenSSLError(int err) {
43 switch (err) {
44 case SSL_ERROR_WANT_READ:
45 case SSL_ERROR_WANT_WRITE:
46 return ERR_IO_PENDING;
47 default:
48 MaybeLogSSLError();
49 // TODO(joth): Implement full mapping.
50 return err < 0 ? ERR_SSL_PROTOCOL_ERROR : err;
wtc 2010/09/28 18:08:35 BUG: This error-mapping function should never re
joth 2010/09/29 11:58:54 Done.
51 }
52 }
53
54 } // namespace
55
56 SSL_CTX* SSLClientSocketOpenSSL::g_ctx = NULL;
wtc 2010/09/28 18:08:35 Nit: we usually add a comment // static before s
joth 2010/09/29 11:58:54 Done.
57
58 SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
59 ClientSocketHandle* transport_socket,
60 const std::string& hostname,
61 const SSLConfig& ssl_config)
62 : ALLOW_THIS_IN_INITIALIZER_LIST(buffer_send_callback_(
63 this, &SSLClientSocketOpenSSL::BufferSendComplete)),
64 ALLOW_THIS_IN_INITIALIZER_LIST(buffer_recv_callback_(
65 this, &SSLClientSocketOpenSSL::BufferRecvComplete)),
66 transport_send_busy_(false),
67 transport_recv_busy_(false),
68 user_connect_callback_(NULL),
69 user_read_callback_(NULL),
70 user_write_callback_(NULL),
71 client_auth_cert_needed_(false),
72 ssl_(NULL),
73 transport_bio_(NULL),
74 transport_(transport_socket),
75 hostname_(hostname),
76 ssl_config_(ssl_config),
77 completed_handshake_(false),
78 net_log_(transport_socket->socket()->NetLog()) {
79 }
80
81 SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() {
82 Disconnect();
83 }
84
85 bool SSLClientSocketOpenSSL::InitOpenSSL() {
86 if (g_ctx) {
wtc 2010/09/28 18:08:35 Nit: in the net module we usually omit curly brace
joth 2010/09/29 11:58:54 Done.
87 return true;
88 }
89
90 SSL_load_error_strings();
91 MaybeLogSSLError();
92 SSL_library_init();
93 MaybeLogSSLError();
94
95 g_ctx = SSL_CTX_new(TLSv1_client_method());
96
97 if (!g_ctx) {
98 MaybeLogSSLError();
99 return false;
100 }
101
102 SSL_CTX_set_verify(g_ctx, SSL_VERIFY_NONE, NULL /*callback*/);
103
104 return true;
105 }
106
107 bool SSLClientSocketOpenSSL::Init() {
108 DCHECK(g_ctx);
109 ssl_ = SSL_new(g_ctx);
110 if (!ssl_) {
111 MaybeLogSSLError();
112 return false;
113 }
114
115 if (!SSL_set_tlsext_host_name(ssl_, hostname_.c_str())) {
116 MaybeLogSSLError();
117 return false;
118 }
119
120 BIO* ssl_bio = NULL;
121 // TODO(joth): Provide explicit write buffer sizes, rather than use defaults?
122 if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0)) {
123 MaybeLogSSLError();
124 return false;
125 }
126 DCHECK(ssl_bio);
127 DCHECK(transport_bio_);
128
129 SSL_set_bio(ssl_, ssl_bio, ssl_bio);
130
131 return true;
132 }
133
134 // SSLClientSocket methods
135
136 void SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
137 // Chrome DCHECKs that https pages provide a valid cert. For now (whilst in
138 // early dev) we just create a spoof cert.
139 // TODO(joth): implement X509Certificate for OpenSSL and remove this hack.
140 LOG(WARNING) << "Filling in certificate with bogus content";
141 ssl_info->Reset();
142 ssl_info->cert = X509Certificate::CreateFromBytes(
wtc 2010/09/28 18:08:35 There is a better way to construct a fake certific
joth 2010/09/29 11:58:54 Hmmm couldn't find any reference to X509 in gears
wtc 2010/09/29 18:21:05 I should have said more about how to find those fi
143 reinterpret_cast<const char*>(google_der), sizeof(google_der));
144 DCHECK(ssl_info->cert);
145 ssl_info->cert_status = 0;
146 ssl_info->security_bits = 128;
147
148 ssl_info->connection_status =
149 ((TLS1_CK_RSA_EXPORT1024_WITH_DES_CBC_SHA) &
wtc 2010/09/28 18:08:35 Nit: this fake cipher suite is inconsistent with t
joth 2010/09/29 11:58:54 Done. (although, I kind of like that this bogus in
150 SSL_CONNECTION_CIPHERSUITE_MASK) << SSL_CONNECTION_CIPHERSUITE_SHIFT;
151
152 // Silence compiler about unused vars.
153 (void)google_der;
154 (void)webkit_der;
155 (void)thawte_der;
156 (void)paypal_null_der;
157 }
158
159 void SSLClientSocketOpenSSL::GetSSLCertRequestInfo(
160 SSLCertRequestInfo* cert_request_info) {
161 NOTREACHED();
162 }
163
164 SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto(
165 std::string* proto) {
166 proto->clear();
167 return kNextProtoUnsupported;
168 }
169
170 void SSLClientSocketOpenSSL::DoReadCallback(int rv) {
171 // Since Run may result in Read being called, clear |user_read_callback_|
172 // up front.
173 CompletionCallback* c = user_read_callback_;
174 user_read_callback_ = NULL;
175 user_read_buf_ = NULL;
176 user_read_buf_len_ = 0;
177 c->Run(rv);
178 }
179
180 void SSLClientSocketOpenSSL::DoWriteCallback(int rv) {
181 // Since Run may result in Write being called, clear |user_write_callback_|
182 // up front.
183 CompletionCallback* c = user_write_callback_;
184 user_write_callback_ = NULL;
185 user_write_buf_ = NULL;
186 user_write_buf_len_ = 0;
187 c->Run(rv);
188 }
189
190 // ClientSocket methods
191
192 // Should make a proper return error
193 // and call the callback with more info
wtc 2010/09/28 18:08:35 Nit: my C++ readability review told me to use prop
joth 2010/09/29 11:58:54 Removed obsolete comment.
194 int SSLClientSocketOpenSSL::Connect(CompletionCallback* callback) {
195 net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT, NULL);
196
197 if (!InitOpenSSL()) {
198 net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
199 return ERR_UNEXPECTED;
200 }
201
202 // Set up new ssl object
203 if (!Init()) {
204 net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
205 return ERR_UNEXPECTED;
206 }
207
208 // Set SSL to client mode. Handshake happens in the loop below.
209 SSL_set_connect_state(ssl_);
210
211 GotoState(STATE_HANDSHAKE);
212 int rv = DoHandshakeLoop(net::OK);
213 if (rv == ERR_IO_PENDING) {
214 user_connect_callback_ = callback;
215 } else {
216 net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
217 }
218
219 // (Optional) Tell loadlog we are starting and store a ref
wtc 2010/09/28 18:08:35 I have no idea what this comment means ...
joth 2010/09/29 11:58:54 Removed obsolete comment.
220 return rv > OK ? OK : rv;
221 }
222
223 void SSLClientSocketOpenSSL::Disconnect() {
224 // Null all callbacks
225 // Delete all buffers
226 transport_send_busy_ = false;
227 send_buffer_ = NULL;
228 transport_recv_busy_ = false;
229 recv_buffer_ = NULL;
230
231 user_connect_callback_ = NULL;
232 user_read_callback_ = NULL;
233 user_write_callback_ = NULL;
234 user_read_buf_ = NULL;
235 user_read_buf_len_ = 0;
236 user_write_buf_ = NULL;
237 user_write_buf_len_ = 0;
238
239 client_certs_.clear();
240 client_auth_cert_needed_ = false;
241
242 if (ssl_) {
243 SSL_free(ssl_);
244 ssl_ = NULL;
245 }
246 if (transport_bio_) {
247 BIO_free(transport_bio_);
248 transport_bio_ = NULL;
249 }
250
251 completed_handshake_ = false;
252 transport_->socket()->Disconnect();
253 }
254
255 int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) {
256 bool network_moved;
257 int rv = last_io_result;
258 do {
259 // Default to STATE_NONE for next state.
260 // (This is a quirk carried over from the windows
261 // implementation. It makes reading the logs a bit harder.)
262 // State handlers can and often do call GotoState just
263 // to stay in the current state.
264 State state = next_handshake_state_;
265 GotoState(STATE_NONE);
266 switch (state) {
267 case STATE_NONE:
268 // we're just pumping data between the buffer and the network
269 break;
270 case STATE_HANDSHAKE:
271 rv = DoHandshake();
272 break;
273 #if 0 // TODO(joth): Implement cert verification.
274 case STATE_VERIFY_CERT:
275 DCHECK(rv == OK);
276 rv = DoVerifyCert(rv);
277 break;
278 case STATE_VERIFY_CERT_COMPLETE:
279 rv = DoVerifyCertComplete(rv);
280 break;
281 #endif
282 default:
283 rv = ERR_UNEXPECTED;
284 NOTREACHED() << "unexpected state" << state;
285 break;
286 }
287
288 // To avoid getting an ERR_IO_PENDING here after handshake complete.
289 if (next_handshake_state_ == STATE_NONE)
290 break;
291
292 // Do the actual network I/O
293 network_moved = DoTransportIO();
294 } while ((rv != ERR_IO_PENDING || network_moved) &&
295 next_handshake_state_ != STATE_NONE);
296 return rv;
297 }
298
299 int SSLClientSocketOpenSSL::DoHandshake() {
300 int net_error = net::OK;
301 int rv = SSL_do_handshake(ssl_);
302
303 if (rv == 1) {
304 // SSL handshake is completed. Let's verify the certificate.
305 // For now we are done, certificates not implemented yet
306 // TODO(joth): Implement certificates
307 GotoState(STATE_NONE);
308 completed_handshake_ = true;
309 } else {
310 int ssl_error = SSL_get_error(ssl_, rv);
311
312 // If not done, stay in this state
313 GotoState(STATE_HANDSHAKE);
314 net_error = MapOpenSSLError(ssl_error);
315 }
316
317 return net_error;
318 }
319
320 bool SSLClientSocketOpenSSL::DoTransportIO() {
321 bool network_moved = false;
322 int nsent = BufferSend();
323 int nreceived = BufferRecv();
324 network_moved = (nsent > 0 || nreceived >= 0);
325 return network_moved;
326 }
327
328 int SSLClientSocketOpenSSL::BufferSend(void) {
329 if (transport_send_busy_)
330 return ERR_IO_PENDING;
331
332 if (!send_buffer_) {
333 // Get a fresh send buffer out of the send BIO.
334 size_t max_read = BIO_ctrl_pending(transport_bio_);
335 if (max_read > 0) {
336 send_buffer_ = new DrainableIOBuffer(new IOBuffer(max_read), max_read);
337 int read_bytes = BIO_read(transport_bio_, send_buffer_->data(), max_read);
338 DCHECK_GT(read_bytes, 0);
339 CHECK_EQ(static_cast<int>(max_read), read_bytes);
340 }
341 }
342
343 int rv = 0;
344 while (send_buffer_) {
345 rv = transport_->socket()->Write(send_buffer_,
346 send_buffer_->BytesRemaining(),
347 &buffer_send_callback_);
348 if (rv == ERR_IO_PENDING) {
349 transport_send_busy_ = true;
350 return rv;
351 }
352 TransportWriteComplete(rv);
353 }
354 return rv;
355 }
356
357 void SSLClientSocketOpenSSL::BufferSendComplete(int result) {
358 transport_send_busy_ = false;
359 TransportWriteComplete(result);
360 OnSendComplete(result);
361 }
362
363 void SSLClientSocketOpenSSL::TransportWriteComplete(int result) {
364 if (result < 0) {
365 // Got a socket write error; close the BIO to indicate this upward.
366 (void)BIO_shutdown_wr(transport_bio_);
367 send_buffer_ = NULL;
368 } else {
369 DCHECK(send_buffer_);
370 send_buffer_->DidConsume(result);
371 DCHECK_GE(send_buffer_->BytesRemaining(), 0);
372 if (send_buffer_->BytesRemaining() <= 0)
373 send_buffer_ = NULL;
374 }
375 }
376
377 int SSLClientSocketOpenSSL::BufferRecv(void) {
378 if (transport_recv_busy_)
379 return ERR_IO_PENDING;
380
381 size_t max_write = BIO_ctrl_get_write_guarantee(transport_bio_);
382 if (max_write > kMaxRecvBufferSize)
383 max_write = kMaxRecvBufferSize;
384
385 if (!max_write) {
386 return ERR_IO_PENDING;
387 }
388 recv_buffer_ = new IOBuffer(max_write);
389 int rv = transport_->socket()->Read(recv_buffer_, max_write,
390 &buffer_recv_callback_);
391 if (rv == ERR_IO_PENDING) {
392 transport_recv_busy_ = true;
393 } else {
394 TransportReadComplete(rv);
395 }
396 return rv;
397 }
398
399 void SSLClientSocketOpenSSL::BufferRecvComplete(int result) {
400 TransportReadComplete(result);
401 OnRecvComplete(result);
402 }
403
404 void SSLClientSocketOpenSSL::TransportReadComplete(int result) {
405 if (result > 0) {
406 int ret = BIO_write(transport_bio_, recv_buffer_->data(), result);
407 // A write into a memory BIO should always succeed.
408 CHECK_EQ(result, ret);
409 } else {
410 // Received end of file: bubble it up to the SSL layer via the BIO.
411 BIO_set_mem_eof_return(transport_bio_, 0);
412 (void)BIO_shutdown_wr(transport_bio_);
413 }
414 recv_buffer_ = NULL;
415 transport_recv_busy_ = false;
416 }
417
418 void SSLClientSocketOpenSSL::DoConnectCallback(int rv) {
419 CompletionCallback* c = user_connect_callback_;
420 user_connect_callback_ = NULL;
421 c->Run(rv > OK ? OK : rv);
422 }
423
424 void SSLClientSocketOpenSSL::OnHandshakeIOComplete(int result) {
425 int rv = DoHandshakeLoop(result);
426 if (rv != ERR_IO_PENDING) {
427 net_log_.EndEvent(NetLog::TYPE_SSL_CONNECT, NULL);
428 DoConnectCallback(rv);
429 }
430 }
431
432 void SSLClientSocketOpenSSL::OnSendComplete(int result) {
433 if (next_handshake_state_ != STATE_NONE) {
434 // In handshake phase.
435 OnHandshakeIOComplete(result);
436 return;
437 }
438
439 // OnSendComplete may need to call DoPayloadRead while the renegotiation
440 // handshake is in progress.
441 int rv_read = ERR_IO_PENDING;
442 int rv_write = ERR_IO_PENDING;
443 bool network_moved;
444 do {
445 if (user_read_buf_)
446 rv_read = DoPayloadRead();
447 if (user_write_buf_)
448 rv_write = DoPayloadWrite();
449 network_moved = DoTransportIO();
450 } while (rv_read == ERR_IO_PENDING &&
451 rv_write == ERR_IO_PENDING &&
452 network_moved);
453
454 if (user_read_buf_ && rv_read != ERR_IO_PENDING)
455 DoReadCallback(rv_read);
456 if (user_write_buf_ && rv_write != ERR_IO_PENDING)
457 DoWriteCallback(rv_write);
458 }
459
460 void SSLClientSocketOpenSSL::OnRecvComplete(int result) {
461 if (next_handshake_state_ != STATE_NONE) {
462 // In handshake phase.
463 OnHandshakeIOComplete(result);
464 return;
465 }
466
467 // Network layer received some data, check if client requested to read
468 // decrypted data.
469 if (!user_read_buf_) {
470 return;
471 }
472
473 int rv = DoReadLoop(result);
474 if (rv != ERR_IO_PENDING)
475 DoReadCallback(rv);
476 }
477
478 bool SSLClientSocketOpenSSL::IsConnected() const {
479 bool ret = completed_handshake_ && transport_->socket()->IsConnected();
480 return ret;
481 }
482
483 bool SSLClientSocketOpenSSL::IsConnectedAndIdle() const {
484 bool ret = completed_handshake_ && transport_->socket()->IsConnectedAndIdle();
485 return ret;
486 }
487
488 int SSLClientSocketOpenSSL::GetPeerAddress(AddressList* addressList) const {
489 return transport_->socket()->GetPeerAddress(addressList);
490 }
491
492 const BoundNetLog& SSLClientSocketOpenSSL::NetLog() const {
493 return net_log_;
494 }
495
496 void SSLClientSocketOpenSSL::SetSubresourceSpeculation() {
497 if (transport_.get() && transport_->socket()) {
498 transport_->socket()->SetSubresourceSpeculation();
499 } else {
500 NOTREACHED();
501 }
502 }
503
504 void SSLClientSocketOpenSSL::SetOmniboxSpeculation() {
505 if (transport_.get() && transport_->socket()) {
506 transport_->socket()->SetOmniboxSpeculation();
507 } else {
508 NOTREACHED();
509 }
510 }
511
512 bool SSLClientSocketOpenSSL::WasEverUsed() const {
513 if (transport_.get() && transport_->socket()) {
514 return transport_->socket()->WasEverUsed();
515 }
516 NOTREACHED();
517 return false;
518 }
519
520 // Socket methods
521
522 int SSLClientSocketOpenSSL::Read(IOBuffer* buf,
523 int buf_len,
524 CompletionCallback* callback) {
525 user_read_buf_ = buf;
526 user_read_buf_len_ = buf_len;
527
528 int rv = DoReadLoop(OK);
529
530 if (rv == ERR_IO_PENDING) {
531 user_read_callback_ = callback;
532 } else {
533 user_read_buf_ = NULL;
534 user_read_buf_len_ = 0;
535 }
536
537 return rv;
538 }
539
540 int SSLClientSocketOpenSSL::DoReadLoop(int result) {
541 if (result < 0)
542 return result;
543
544 bool network_moved;
545 int rv;
546 do {
547 rv = DoPayloadRead();
548 network_moved = DoTransportIO();
549 } while (rv == ERR_IO_PENDING && network_moved);
550
551 return rv;
552 }
553
554 int SSLClientSocketOpenSSL::Write(IOBuffer* buf,
555 int buf_len,
556 CompletionCallback* callback) {
557 user_write_buf_ = buf;
558 user_write_buf_len_ = buf_len;
559
560 int rv = DoWriteLoop(OK);
561
562 if (rv == ERR_IO_PENDING) {
563 user_write_callback_ = callback;
564 } else {
565 user_write_buf_ = NULL;
566 user_write_buf_len_ = 0;
567 }
568
569 return rv;
570 }
571
572 int SSLClientSocketOpenSSL::DoWriteLoop(int result) {
573 if (result < 0)
574 return result;
575
576 bool network_moved;
577 int rv;
578 do {
579 rv = DoPayloadWrite();
580 network_moved = DoTransportIO();
581 } while (rv == ERR_IO_PENDING && network_moved);
582
583 return rv;
584 }
585
586 bool SSLClientSocketOpenSSL::SetReceiveBufferSize(int32 size) {
587 return transport_->socket()->SetReceiveBufferSize(size);
588 }
589
590 bool SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) {
591 return transport_->socket()->SetSendBufferSize(size);
592 }
593
594 int SSLClientSocketOpenSSL::DoPayloadRead() {
595 int rv = SSL_read(ssl_, user_read_buf_->data(), user_read_buf_len_);
596 if (client_auth_cert_needed_) {
597 // We don't need to invalidate the non-client-authenticated SSL session
598 // because the server will renegotiate anyway.
599 return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
600 }
601
602 if (rv >= 0) {
603 return rv;
604 }
605
606 int err = SSL_get_error(ssl_, rv);
607 return MapOpenSSLError(err);
608 }
609
610 int SSLClientSocketOpenSSL::DoPayloadWrite() {
611 int rv = SSL_write(ssl_, user_write_buf_->data(), user_write_buf_len_);
612
613 if (rv >= 0) {
614 return rv;
615 }
616
617 int err = SSL_get_error(ssl_, rv);
618 return MapOpenSSLError(err);
619 }
620
621 } // namespace net
OLDNEW
« net/socket/ssl_client_socket_openssl.h ('K') | « net/socket/ssl_client_socket_openssl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698