blob: 117dc565b6c316ccc2b4449667805f56174e6426 [file] [log] [blame]
Victor Stinner231b4042015-01-14 00:19:09 +01001import collections
2try:
3 import ssl
4except ImportError: # pragma: no cover
5 ssl = None
6
7from . import protocols
8from . import transports
9from .log import logger
10
11
12def _create_transport_context(server_side, server_hostname):
13 if server_side:
14 raise ValueError('Server side SSL needs a valid SSLContext')
15
16 # Client side may pass ssl=True to use a default
17 # context; in that case the sslcontext passed is None.
18 # The default is secure for client connections.
19 if hasattr(ssl, 'create_default_context'):
20 # Python 3.4+: use up-to-date strong settings.
21 sslcontext = ssl.create_default_context()
22 if not server_hostname:
23 sslcontext.check_hostname = False
24 else:
25 # Fallback for Python 3.3.
26 sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
27 sslcontext.options |= ssl.OP_NO_SSLv2
28 sslcontext.options |= ssl.OP_NO_SSLv3
29 sslcontext.set_default_verify_paths()
30 sslcontext.verify_mode = ssl.CERT_REQUIRED
31 return sslcontext
32
33
34def _is_sslproto_available():
35 return hasattr(ssl, "MemoryBIO")
36
37
38# States of an _SSLPipe.
39_UNWRAPPED = "UNWRAPPED"
40_DO_HANDSHAKE = "DO_HANDSHAKE"
41_WRAPPED = "WRAPPED"
42_SHUTDOWN = "SHUTDOWN"
43
44
45class _SSLPipe(object):
46 """An SSL "Pipe".
47
48 An SSL pipe allows you to communicate with an SSL/TLS protocol instance
49 through memory buffers. It can be used to implement a security layer for an
50 existing connection where you don't have access to the connection's file
51 descriptor, or for some reason you don't want to use it.
52
53 An SSL pipe can be in "wrapped" and "unwrapped" mode. In unwrapped mode,
54 data is passed through untransformed. In wrapped mode, application level
55 data is encrypted to SSL record level data and vice versa. The SSL record
56 level is the lowest level in the SSL protocol suite and is what travels
57 as-is over the wire.
58
59 An SslPipe initially is in "unwrapped" mode. To start SSL, call
60 do_handshake(). To shutdown SSL again, call unwrap().
61 """
62
63 max_size = 256 * 1024 # Buffer size passed to read()
64
65 def __init__(self, context, server_side, server_hostname=None):
66 """
67 The *context* argument specifies the ssl.SSLContext to use.
68
69 The *server_side* argument indicates whether this is a server side or
70 client side transport.
71
72 The optional *server_hostname* argument can be used to specify the
73 hostname you are connecting to. You may only specify this parameter if
74 the _ssl module supports Server Name Indication (SNI).
75 """
76 self._context = context
77 self._server_side = server_side
78 self._server_hostname = server_hostname
79 self._state = _UNWRAPPED
80 self._incoming = ssl.MemoryBIO()
81 self._outgoing = ssl.MemoryBIO()
82 self._sslobj = None
83 self._need_ssldata = False
84 self._handshake_cb = None
85 self._shutdown_cb = None
86
87 @property
88 def context(self):
89 """The SSL context passed to the constructor."""
90 return self._context
91
92 @property
93 def ssl_object(self):
94 """The internal ssl.SSLObject instance.
95
96 Return None if the pipe is not wrapped.
97 """
98 return self._sslobj
99
100 @property
101 def need_ssldata(self):
102 """Whether more record level data is needed to complete a handshake
103 that is currently in progress."""
104 return self._need_ssldata
105
106 @property
107 def wrapped(self):
108 """
109 Whether a security layer is currently in effect.
110
111 Return False during handshake.
112 """
113 return self._state == _WRAPPED
114
115 def do_handshake(self, callback=None):
116 """Start the SSL handshake.
117
118 Return a list of ssldata. A ssldata element is a list of buffers
119
120 The optional *callback* argument can be used to install a callback that
121 will be called when the handshake is complete. The callback will be
122 called with None if successful, else an exception instance.
123 """
124 if self._state != _UNWRAPPED:
125 raise RuntimeError('handshake in progress or completed')
126 self._sslobj = self._context.wrap_bio(
127 self._incoming, self._outgoing,
128 server_side=self._server_side,
129 server_hostname=self._server_hostname)
130 self._state = _DO_HANDSHAKE
131 self._handshake_cb = callback
132 ssldata, appdata = self.feed_ssldata(b'', only_handshake=True)
133 assert len(appdata) == 0
134 return ssldata
135
136 def shutdown(self, callback=None):
137 """Start the SSL shutdown sequence.
138
139 Return a list of ssldata. A ssldata element is a list of buffers
140
141 The optional *callback* argument can be used to install a callback that
142 will be called when the shutdown is complete. The callback will be
143 called without arguments.
144 """
145 if self._state == _UNWRAPPED:
146 raise RuntimeError('no security layer present')
147 if self._state == _SHUTDOWN:
148 raise RuntimeError('shutdown in progress')
149 assert self._state in (_WRAPPED, _DO_HANDSHAKE)
150 self._state = _SHUTDOWN
151 self._shutdown_cb = callback
152 ssldata, appdata = self.feed_ssldata(b'')
153 assert appdata == [] or appdata == [b'']
154 return ssldata
155
156 def feed_eof(self):
157 """Send a potentially "ragged" EOF.
158
159 This method will raise an SSL_ERROR_EOF exception if the EOF is
160 unexpected.
161 """
162 self._incoming.write_eof()
163 ssldata, appdata = self.feed_ssldata(b'')
164 assert appdata == [] or appdata == [b'']
165
166 def feed_ssldata(self, data, only_handshake=False):
167 """Feed SSL record level data into the pipe.
168
169 The data must be a bytes instance. It is OK to send an empty bytes
170 instance. This can be used to get ssldata for a handshake initiated by
171 this endpoint.
172
173 Return a (ssldata, appdata) tuple. The ssldata element is a list of
174 buffers containing SSL data that needs to be sent to the remote SSL.
175
176 The appdata element is a list of buffers containing plaintext data that
177 needs to be forwarded to the application. The appdata list may contain
178 an empty buffer indicating an SSL "close_notify" alert. This alert must
179 be acknowledged by calling shutdown().
180 """
181 if self._state == _UNWRAPPED:
182 # If unwrapped, pass plaintext data straight through.
183 if data:
184 appdata = [data]
185 else:
186 appdata = []
187 return ([], appdata)
188
189 self._need_ssldata = False
190 if data:
191 self._incoming.write(data)
192
193 ssldata = []
194 appdata = []
195 try:
196 if self._state == _DO_HANDSHAKE:
197 # Call do_handshake() until it doesn't raise anymore.
198 self._sslobj.do_handshake()
199 self._state = _WRAPPED
200 if self._handshake_cb:
201 self._handshake_cb(None)
202 if only_handshake:
203 return (ssldata, appdata)
204 # Handshake done: execute the wrapped block
205
206 if self._state == _WRAPPED:
207 # Main state: read data from SSL until close_notify
208 while True:
209 chunk = self._sslobj.read(self.max_size)
210 appdata.append(chunk)
211 if not chunk: # close_notify
212 break
213
214 elif self._state == _SHUTDOWN:
215 # Call shutdown() until it doesn't raise anymore.
216 self._sslobj.unwrap()
217 self._sslobj = None
218 self._state = _UNWRAPPED
219 if self._shutdown_cb:
220 self._shutdown_cb()
221
222 elif self._state == _UNWRAPPED:
223 # Drain possible plaintext data after close_notify.
224 appdata.append(self._incoming.read())
225 except (ssl.SSLError, ssl.CertificateError) as exc:
226 if getattr(exc, 'errno', None) not in (
227 ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE,
228 ssl.SSL_ERROR_SYSCALL):
229 if self._state == _DO_HANDSHAKE and self._handshake_cb:
230 self._handshake_cb(exc)
231 raise
232 self._need_ssldata = (exc.errno == ssl.SSL_ERROR_WANT_READ)
233
234 # Check for record level data that needs to be sent back.
235 # Happens for the initial handshake and renegotiations.
236 if self._outgoing.pending:
237 ssldata.append(self._outgoing.read())
238 return (ssldata, appdata)
239
240 def feed_appdata(self, data, offset=0):
241 """Feed plaintext data into the pipe.
242
243 Return an (ssldata, offset) tuple. The ssldata element is a list of
244 buffers containing record level data that needs to be sent to the
245 remote SSL instance. The offset is the number of plaintext bytes that
246 were processed, which may be less than the length of data.
247
248 NOTE: In case of short writes, this call MUST be retried with the SAME
249 buffer passed into the *data* argument (i.e. the id() must be the
250 same). This is an OpenSSL requirement. A further particularity is that
251 a short write will always have offset == 0, because the _ssl module
252 does not enable partial writes. And even though the offset is zero,
253 there will still be encrypted data in ssldata.
254 """
255 assert 0 <= offset <= len(data)
256 if self._state == _UNWRAPPED:
257 # pass through data in unwrapped mode
258 if offset < len(data):
259 ssldata = [data[offset:]]
260 else:
261 ssldata = []
262 return (ssldata, len(data))
263
264 ssldata = []
265 view = memoryview(data)
266 while True:
267 self._need_ssldata = False
268 try:
269 if offset < len(view):
270 offset += self._sslobj.write(view[offset:])
271 except ssl.SSLError as exc:
272 # It is not allowed to call write() after unwrap() until the
273 # close_notify is acknowledged. We return the condition to the
274 # caller as a short write.
275 if exc.reason == 'PROTOCOL_IS_SHUTDOWN':
276 exc.errno = ssl.SSL_ERROR_WANT_READ
277 if exc.errno not in (ssl.SSL_ERROR_WANT_READ,
278 ssl.SSL_ERROR_WANT_WRITE,
279 ssl.SSL_ERROR_SYSCALL):
280 raise
281 self._need_ssldata = (exc.errno == ssl.SSL_ERROR_WANT_READ)
282
283 # See if there's any record level data back for us.
284 if self._outgoing.pending:
285 ssldata.append(self._outgoing.read())
286 if offset == len(view) or self._need_ssldata:
287 break
288 return (ssldata, offset)
289
290
291class _SSLProtocolTransport(transports._FlowControlMixin,
292 transports.Transport):
293
294 def __init__(self, loop, ssl_protocol, app_protocol):
295 self._loop = loop
296 self._ssl_protocol = ssl_protocol
297 self._app_protocol = app_protocol
298
299 def get_extra_info(self, name, default=None):
300 """Get optional transport information."""
301 return self._ssl_protocol._get_extra_info(name, default)
302
303 def close(self):
304 """Close the transport.
305
306 Buffered data will be flushed asynchronously. No more data
307 will be received. After all buffered data is flushed, the
308 protocol's connection_lost() method will (eventually) called
309 with None as its argument.
310 """
311 self._ssl_protocol._start_shutdown()
312
313 def pause_reading(self):
314 """Pause the receiving end.
315
316 No data will be passed to the protocol's data_received()
317 method until resume_reading() is called.
318 """
319 self._ssl_protocol._transport.pause_reading()
320
321 def resume_reading(self):
322 """Resume the receiving end.
323
324 Data received will once again be passed to the protocol's
325 data_received() method.
326 """
327 self._ssl_protocol._transport.resume_reading()
328
329 def set_write_buffer_limits(self, high=None, low=None):
330 """Set the high- and low-water limits for write flow control.
331
332 These two values control when to call the protocol's
333 pause_writing() and resume_writing() methods. If specified,
334 the low-water limit must be less than or equal to the
335 high-water limit. Neither value can be negative.
336
337 The defaults are implementation-specific. If only the
338 high-water limit is given, the low-water limit defaults to a
339 implementation-specific value less than or equal to the
340 high-water limit. Setting high to zero forces low to zero as
341 well, and causes pause_writing() to be called whenever the
342 buffer becomes non-empty. Setting low to zero causes
343 resume_writing() to be called only once the buffer is empty.
344 Use of zero for either limit is generally sub-optimal as it
345 reduces opportunities for doing I/O and computation
346 concurrently.
347 """
348 self._ssl_protocol._transport.set_write_buffer_limits(high, low)
349
350 def get_write_buffer_size(self):
351 """Return the current size of the write buffer."""
352 return self._ssl_protocol._transport.get_write_buffer_size()
353
354 def write(self, data):
355 """Write some data bytes to the transport.
356
357 This does not block; it buffers the data and arranges for it
358 to be sent out asynchronously.
359 """
360 if not isinstance(data, (bytes, bytearray, memoryview)):
361 raise TypeError("data: expecting a bytes-like instance, got {!r}"
362 .format(type(data).__name__))
363 if not data:
364 return
365 self._ssl_protocol._write_appdata(data)
366
367 def can_write_eof(self):
368 """Return True if this transport supports write_eof(), False if not."""
369 return False
370
371 def abort(self):
372 """Close the transport immediately.
373
374 Buffered data will be lost. No more data will be received.
375 The protocol's connection_lost() method will (eventually) be
376 called with None as its argument.
377 """
378 self._ssl_protocol._abort()
379
380
381class SSLProtocol(protocols.Protocol):
382 """SSL protocol.
383
384 Implementation of SSL on top of a socket using incoming and outgoing
385 buffers which are ssl.MemoryBIO objects.
386 """
387
388 def __init__(self, loop, app_protocol, sslcontext, waiter,
389 server_side=False, server_hostname=None):
390 if ssl is None:
391 raise RuntimeError('stdlib ssl module not available')
392
393 if not sslcontext:
394 sslcontext = _create_transport_context(server_side, server_hostname)
395
396 self._server_side = server_side
397 if server_hostname and not server_side:
398 self._server_hostname = server_hostname
399 else:
400 self._server_hostname = None
401 self._sslcontext = sslcontext
402 # SSL-specific extra info. More info are set when the handshake
403 # completes.
404 self._extra = dict(sslcontext=sslcontext)
405
406 # App data write buffering
407 self._write_backlog = collections.deque()
408 self._write_buffer_size = 0
409
410 self._waiter = waiter
411 self._closing = False
412 self._loop = loop
413 self._app_protocol = app_protocol
414 self._app_transport = _SSLProtocolTransport(self._loop,
415 self, self._app_protocol)
416 self._sslpipe = None
417 self._session_established = False
418 self._in_handshake = False
419 self._in_shutdown = False
Victor Stinner7e222f42015-01-15 13:16:27 +0100420 self._transport = None
Victor Stinner231b4042015-01-14 00:19:09 +0100421
422 def connection_made(self, transport):
423 """Called when the low-level connection is made.
424
425 Start the SSL handshake.
426 """
427 self._transport = transport
428 self._sslpipe = _SSLPipe(self._sslcontext,
429 self._server_side,
430 self._server_hostname)
431 self._start_handshake()
432
433 def connection_lost(self, exc):
434 """Called when the low-level connection is lost or closed.
435
436 The argument is an exception object or None (the latter
437 meaning a regular EOF is received or the connection was
438 aborted or closed).
439 """
440 if self._session_established:
441 self._session_established = False
442 self._loop.call_soon(self._app_protocol.connection_lost, exc)
443 self._transport = None
444 self._app_transport = None
445
446 def pause_writing(self):
447 """Called when the low-level transport's buffer goes over
448 the high-water mark.
449 """
450 self._app_protocol.pause_writing()
451
452 def resume_writing(self):
453 """Called when the low-level transport's buffer drains below
454 the low-water mark.
455 """
456 self._app_protocol.resume_writing()
457
458 def data_received(self, data):
459 """Called when some SSL data is received.
460
461 The argument is a bytes object.
462 """
463 try:
464 ssldata, appdata = self._sslpipe.feed_ssldata(data)
465 except ssl.SSLError as e:
466 if self._loop.get_debug():
467 logger.warning('%r: SSL error %s (reason %s)',
468 self, e.errno, e.reason)
469 self._abort()
470 return
471
472 for chunk in ssldata:
473 self._transport.write(chunk)
474
475 for chunk in appdata:
476 if chunk:
477 self._app_protocol.data_received(chunk)
478 else:
479 self._start_shutdown()
480 break
481
482 def eof_received(self):
483 """Called when the other end of the low-level stream
484 is half-closed.
485
486 If this returns a false value (including None), the transport
487 will close itself. If it returns a true value, closing the
488 transport is up to the protocol.
489 """
490 try:
491 if self._loop.get_debug():
492 logger.debug("%r received EOF", self)
493 if not self._in_handshake:
494 keep_open = self._app_protocol.eof_received()
495 if keep_open:
496 logger.warning('returning true from eof_received() '
497 'has no effect when using ssl')
498 finally:
499 self._transport.close()
500
501 def _get_extra_info(self, name, default=None):
502 if name in self._extra:
503 return self._extra[name]
504 else:
505 return self._transport.get_extra_info(name, default)
506
507 def _start_shutdown(self):
508 if self._in_shutdown:
509 return
510 self._in_shutdown = True
511 self._write_appdata(b'')
512
513 def _write_appdata(self, data):
514 self._write_backlog.append((data, 0))
515 self._write_buffer_size += len(data)
516 self._process_write_backlog()
517
518 def _start_handshake(self):
519 if self._loop.get_debug():
520 logger.debug("%r starts SSL handshake", self)
521 self._handshake_start_time = self._loop.time()
522 else:
523 self._handshake_start_time = None
524 self._in_handshake = True
525 # (b'', 1) is a special value in _process_write_backlog() to do
526 # the SSL handshake
527 self._write_backlog.append((b'', 1))
528 self._loop.call_soon(self._process_write_backlog)
529
530 def _on_handshake_complete(self, handshake_exc):
531 self._in_handshake = False
532
533 sslobj = self._sslpipe.ssl_object
Victor Stinner231b4042015-01-14 00:19:09 +0100534 try:
535 if handshake_exc is not None:
536 raise handshake_exc
Victor Stinner177e9f02015-01-14 16:56:20 +0100537
538 peercert = sslobj.getpeercert()
Victor Stinner231b4042015-01-14 00:19:09 +0100539 if not hasattr(self._sslcontext, 'check_hostname'):
540 # Verify hostname if requested, Python 3.4+ uses check_hostname
541 # and checks the hostname in do_handshake()
542 if (self._server_hostname
543 and self._sslcontext.verify_mode != ssl.CERT_NONE):
544 ssl.match_hostname(peercert, self._server_hostname)
545 except BaseException as exc:
546 if self._loop.get_debug():
547 if isinstance(exc, ssl.CertificateError):
548 logger.warning("%r: SSL handshake failed "
549 "on verifying the certificate",
550 self, exc_info=True)
551 else:
552 logger.warning("%r: SSL handshake failed",
553 self, exc_info=True)
554 self._transport.close()
555 if isinstance(exc, Exception):
Victor Stinner177e9f02015-01-14 16:56:20 +0100556 if self._waiter is not None and not self._waiter.cancelled():
Victor Stinner231b4042015-01-14 00:19:09 +0100557 self._waiter.set_exception(exc)
558 return
559 else:
560 raise
561
562 if self._loop.get_debug():
563 dt = self._loop.time() - self._handshake_start_time
564 logger.debug("%r: SSL handshake took %.1f ms", self, dt * 1e3)
565
566 # Add extra info that becomes available after handshake.
567 self._extra.update(peercert=peercert,
568 cipher=sslobj.cipher(),
569 compression=sslobj.compression(),
570 )
571 self._app_protocol.connection_made(self._app_transport)
572 if self._waiter is not None:
573 # wait until protocol.connection_made() has been called
574 self._waiter._set_result_unless_cancelled(None)
575 self._session_established = True
Victor Stinner042dad72015-01-15 09:41:48 +0100576 # In case transport.write() was already called. Don't call
577 # immediatly _process_write_backlog(), but schedule it:
578 # _on_handshake_complete() can be called indirectly from
579 # _process_write_backlog(), and _process_write_backlog() is not
580 # reentrant.
Victor Stinner72bdefb2015-01-15 09:44:13 +0100581 self._loop.call_soon(self._process_write_backlog)
Victor Stinner231b4042015-01-14 00:19:09 +0100582
583 def _process_write_backlog(self):
584 # Try to make progress on the write backlog.
585 if self._transport is None:
586 return
587
588 try:
589 for i in range(len(self._write_backlog)):
590 data, offset = self._write_backlog[0]
591 if data:
592 ssldata, offset = self._sslpipe.feed_appdata(data, offset)
593 elif offset:
594 ssldata = self._sslpipe.do_handshake(self._on_handshake_complete)
595 offset = 1
596 else:
597 ssldata = self._sslpipe.shutdown(self._finalize)
598 offset = 1
599
600 for chunk in ssldata:
601 self._transport.write(chunk)
602
603 if offset < len(data):
604 self._write_backlog[0] = (data, offset)
605 # A short write means that a write is blocked on a read
606 # We need to enable reading if it is paused!
607 assert self._sslpipe.need_ssldata
608 if self._transport._paused:
609 self._transport.resume_reading()
610 break
611
612 # An entire chunk from the backlog was processed. We can
613 # delete it and reduce the outstanding buffer size.
614 del self._write_backlog[0]
615 self._write_buffer_size -= len(data)
616 except BaseException as exc:
617 if self._in_handshake:
618 self._on_handshake_complete(exc)
619 else:
620 self._fatal_error(exc, 'Fatal error on SSL transport')
621
622 def _fatal_error(self, exc, message='Fatal error on transport'):
623 # Should be called from exception handler only.
624 if isinstance(exc, (BrokenPipeError, ConnectionResetError)):
625 if self._loop.get_debug():
626 logger.debug("%r: %s", self, message, exc_info=True)
627 else:
628 self._loop.call_exception_handler({
629 'message': message,
630 'exception': exc,
631 'transport': self._transport,
632 'protocol': self,
633 })
634 if self._transport:
635 self._transport._force_close(exc)
636
637 def _finalize(self):
638 if self._transport is not None:
639 self._transport.close()
640
641 def _abort(self):
642 if self._transport is not None:
643 try:
644 self._transport.abort()
645 finally:
646 self._finalize()