tls.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936
  1. # -*- test-case-name: twisted.protocols.test.test_tls,twisted.internet.test.test_tls,twisted.test.test_sslverify -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Implementation of a TLS transport (L{ISSLTransport}) as an
  6. L{IProtocol<twisted.internet.interfaces.IProtocol>} layered on top of any
  7. L{ITransport<twisted.internet.interfaces.ITransport>} implementation, based on
  8. U{OpenSSL<http://www.openssl.org>}'s memory BIO features.
  9. L{TLSMemoryBIOFactory} is a L{WrappingFactory} which wraps protocols created by
  10. the factory it wraps with L{TLSMemoryBIOProtocol}. L{TLSMemoryBIOProtocol}
  11. intercedes between the underlying transport and the wrapped protocol to
  12. implement SSL and TLS. Typical usage of this module looks like this::
  13. from twisted.protocols.tls import TLSMemoryBIOFactory
  14. from twisted.internet.protocol import ServerFactory
  15. from twisted.internet.ssl import PrivateCertificate
  16. from twisted.internet import reactor
  17. from someapplication import ApplicationProtocol
  18. serverFactory = ServerFactory()
  19. serverFactory.protocol = ApplicationProtocol
  20. certificate = PrivateCertificate.loadPEM(certPEMData)
  21. contextFactory = certificate.options()
  22. tlsFactory = TLSMemoryBIOFactory(contextFactory, False, serverFactory)
  23. reactor.listenTCP(12345, tlsFactory)
  24. reactor.run()
  25. This API offers somewhat more flexibility than
  26. L{twisted.internet.interfaces.IReactorSSL}; for example, a
  27. L{TLSMemoryBIOProtocol} instance can use another instance of
  28. L{TLSMemoryBIOProtocol} as its transport, yielding TLS over TLS - useful to
  29. implement onion routing. It can also be used to run TLS over unusual
  30. transports, such as UNIX sockets and stdio.
  31. """
  32. from __future__ import annotations
  33. from typing import Callable, Iterable, Optional, cast
  34. from zope.interface import directlyProvides, implementer, providedBy
  35. from OpenSSL.SSL import Connection, Error, SysCallError, WantReadError, ZeroReturnError
  36. from twisted.internet._producer_helpers import _PullToPush
  37. from twisted.internet._sslverify import _setAcceptableProtocols
  38. from twisted.internet.interfaces import (
  39. IDelayedCall,
  40. IHandshakeListener,
  41. ILoggingContext,
  42. INegotiated,
  43. IOpenSSLClientConnectionCreator,
  44. IOpenSSLServerConnectionCreator,
  45. IProtocol,
  46. IProtocolNegotiationFactory,
  47. IPushProducer,
  48. IReactorTime,
  49. ISystemHandle,
  50. ITransport,
  51. )
  52. from twisted.internet.main import CONNECTION_LOST
  53. from twisted.internet.protocol import Protocol
  54. from twisted.protocols.policies import ProtocolWrapper, WrappingFactory
  55. from twisted.python.failure import Failure
  56. @implementer(IPushProducer)
  57. class _ProducerMembrane:
  58. """
  59. Stand-in for producer registered with a L{TLSMemoryBIOProtocol} transport.
  60. Ensures that producer pause/resume events from the undelying transport are
  61. coordinated with pause/resume events from the TLS layer.
  62. @ivar _producer: The application-layer producer.
  63. """
  64. _producerPaused = False
  65. def __init__(self, producer):
  66. self._producer = producer
  67. def pauseProducing(self):
  68. """
  69. C{pauseProducing} the underlying producer, if it's not paused.
  70. """
  71. if self._producerPaused:
  72. return
  73. self._producerPaused = True
  74. self._producer.pauseProducing()
  75. def resumeProducing(self):
  76. """
  77. C{resumeProducing} the underlying producer, if it's paused.
  78. """
  79. if not self._producerPaused:
  80. return
  81. self._producerPaused = False
  82. self._producer.resumeProducing()
  83. def stopProducing(self):
  84. """
  85. C{stopProducing} the underlying producer.
  86. There is only a single source for this event, so it's simply passed
  87. on.
  88. """
  89. self._producer.stopProducing()
  90. def _representsEOF(exceptionObject: Error) -> bool:
  91. """
  92. Does the given OpenSSL.SSL.Error represent an end-of-file?
  93. """
  94. reasonString: str
  95. if isinstance(exceptionObject, SysCallError):
  96. _, reasonString = exceptionObject.args
  97. else:
  98. errorQueue = exceptionObject.args[0]
  99. _, _, reasonString = errorQueue[-1]
  100. return reasonString.casefold().startswith("unexpected eof")
  101. @implementer(ISystemHandle, INegotiated, ITransport)
  102. class TLSMemoryBIOProtocol(ProtocolWrapper):
  103. """
  104. L{TLSMemoryBIOProtocol} is a protocol wrapper which uses OpenSSL via a
  105. memory BIO to encrypt bytes written to it before sending them on to the
  106. underlying transport and decrypts bytes received from the underlying
  107. transport before delivering them to the wrapped protocol.
  108. In addition to producer events from the underlying transport, the need to
  109. wait for reads before a write can proceed means the L{TLSMemoryBIOProtocol}
  110. may also want to pause a producer. Pause/resume events are therefore
  111. merged using the L{_ProducerMembrane} wrapper. Non-streaming (pull)
  112. producers are supported by wrapping them with L{_PullToPush}.
  113. Because TLS may need to wait for reads before writing, some writes may be
  114. buffered until a read occurs.
  115. @ivar _tlsConnection: The L{OpenSSL.SSL.Connection} instance which is
  116. encrypted and decrypting this connection.
  117. @ivar _lostTLSConnection: A flag indicating whether connection loss has
  118. already been dealt with (C{True}) or not (C{False}). TLS disconnection
  119. is distinct from the underlying connection being lost.
  120. @ivar _appSendBuffer: application-level (cleartext) data that is waiting to
  121. be transferred to the TLS buffer, but can't be because the TLS
  122. connection is handshaking.
  123. @type _appSendBuffer: L{list} of L{bytes}
  124. @ivar _connectWrapped: A flag indicating whether or not to call
  125. C{makeConnection} on the wrapped protocol. This is for the reactor's
  126. L{twisted.internet.interfaces.ITLSTransport.startTLS} implementation,
  127. since it has a protocol which it has already called C{makeConnection}
  128. on, and which has no interest in a new transport. See #3821.
  129. @ivar _handshakeDone: A flag indicating whether or not the handshake is
  130. known to have completed successfully (C{True}) or not (C{False}). This
  131. is used to control error reporting behavior. If the handshake has not
  132. completed, the underlying L{OpenSSL.SSL.Error} will be passed to the
  133. application's C{connectionLost} method. If it has completed, any
  134. unexpected L{OpenSSL.SSL.Error} will be turned into a
  135. L{ConnectionLost}. This is weird; however, it is simply an attempt at
  136. a faithful re-implementation of the behavior provided by
  137. L{twisted.internet.ssl}.
  138. @ivar _reason: If an unexpected L{OpenSSL.SSL.Error} occurs which causes
  139. the connection to be lost, it is saved here. If appropriate, this may
  140. be used as the reason passed to the application protocol's
  141. C{connectionLost} method.
  142. @ivar _producer: The current producer registered via C{registerProducer},
  143. or L{None} if no producer has been registered or a previous one was
  144. unregistered.
  145. @ivar _aborted: C{abortConnection} has been called. No further data will
  146. be received to the wrapped protocol's C{dataReceived}.
  147. @type _aborted: L{bool}
  148. """
  149. _reason = None
  150. _handshakeDone = False
  151. _lostTLSConnection = False
  152. _producer = None
  153. _aborted = False
  154. def __init__(self, factory, wrappedProtocol, _connectWrapped=True):
  155. ProtocolWrapper.__init__(self, factory, wrappedProtocol)
  156. self._connectWrapped = _connectWrapped
  157. def getHandle(self):
  158. """
  159. Return the L{OpenSSL.SSL.Connection} object being used to encrypt and
  160. decrypt this connection.
  161. This is done for the benefit of L{twisted.internet.ssl.Certificate}'s
  162. C{peerFromTransport} and C{hostFromTransport} methods only. A
  163. different system handle may be returned by future versions of this
  164. method.
  165. """
  166. return self._tlsConnection
  167. def makeConnection(self, transport):
  168. """
  169. Connect this wrapper to the given transport and initialize the
  170. necessary L{OpenSSL.SSL.Connection} with a memory BIO.
  171. """
  172. self._tlsConnection = self.factory._createConnection(self)
  173. self._appSendBuffer = []
  174. # Add interfaces provided by the transport we are wrapping:
  175. for interface in providedBy(transport):
  176. directlyProvides(self, interface)
  177. # Intentionally skip ProtocolWrapper.makeConnection - it might call
  178. # wrappedProtocol.makeConnection, which we want to make conditional.
  179. Protocol.makeConnection(self, transport)
  180. self.factory.registerProtocol(self)
  181. if self._connectWrapped:
  182. # Now that the TLS layer is initialized, notify the application of
  183. # the connection.
  184. ProtocolWrapper.makeConnection(self, transport)
  185. # Now that we ourselves have a transport (initialized by the
  186. # ProtocolWrapper.makeConnection call above), kick off the TLS
  187. # handshake.
  188. self._checkHandshakeStatus()
  189. def _checkHandshakeStatus(self):
  190. """
  191. Ask OpenSSL to proceed with a handshake in progress.
  192. Initially, this just sends the ClientHello; after some bytes have been
  193. stuffed in to the C{Connection} object by C{dataReceived}, it will then
  194. respond to any C{Certificate} or C{KeyExchange} messages.
  195. """
  196. # The connection might already be aborted (eg. by a callback during
  197. # connection setup), so don't even bother trying to handshake in that
  198. # case.
  199. if self._aborted:
  200. return
  201. try:
  202. self._tlsConnection.do_handshake()
  203. except WantReadError:
  204. self._flushSendBIO()
  205. except Error:
  206. self._tlsShutdownFinished(Failure())
  207. else:
  208. self._handshakeDone = True
  209. if IHandshakeListener.providedBy(self.wrappedProtocol):
  210. self.wrappedProtocol.handshakeCompleted()
  211. def _flushSendBIO(self):
  212. """
  213. Read any bytes out of the send BIO and write them to the underlying
  214. transport.
  215. """
  216. try:
  217. bytes = self._tlsConnection.bio_read(2**15)
  218. except WantReadError:
  219. # There may be nothing in the send BIO right now.
  220. pass
  221. else:
  222. self.transport.write(bytes)
  223. def _flushReceiveBIO(self):
  224. """
  225. Try to receive any application-level bytes which are now available
  226. because of a previous write into the receive BIO. This will take
  227. care of delivering any application-level bytes which are received to
  228. the protocol, as well as handling of the various exceptions which
  229. can come from trying to get such bytes.
  230. """
  231. # Keep trying this until an error indicates we should stop or we
  232. # close the connection. Looping is necessary to make sure we
  233. # process all of the data which was put into the receive BIO, as
  234. # there is no guarantee that a single recv call will do it all.
  235. while not self._lostTLSConnection:
  236. try:
  237. bytes = self._tlsConnection.recv(2**15)
  238. except WantReadError:
  239. # The newly received bytes might not have been enough to produce
  240. # any application data.
  241. break
  242. except ZeroReturnError:
  243. # TLS has shut down and no more TLS data will be received over
  244. # this connection.
  245. self._shutdownTLS()
  246. # Passing in None means the user protocol's connnectionLost
  247. # will get called with reason from underlying transport:
  248. self._tlsShutdownFinished(None)
  249. except Error:
  250. # Something went pretty wrong. For example, this might be a
  251. # handshake failure during renegotiation (because there were no
  252. # shared ciphers, because a certificate failed to verify, etc).
  253. # TLS can no longer proceed.
  254. failure = Failure()
  255. self._tlsShutdownFinished(failure)
  256. else:
  257. if not self._aborted:
  258. ProtocolWrapper.dataReceived(self, bytes)
  259. # The received bytes might have generated a response which needs to be
  260. # sent now. For example, the handshake involves several round-trip
  261. # exchanges without ever producing application-bytes.
  262. self._flushSendBIO()
  263. def dataReceived(self, bytes):
  264. """
  265. Deliver any received bytes to the receive BIO and then read and deliver
  266. to the application any application-level data which becomes available
  267. as a result of this.
  268. """
  269. # Let OpenSSL know some bytes were just received.
  270. self._tlsConnection.bio_write(bytes)
  271. # If we are still waiting for the handshake to complete, try to
  272. # complete the handshake with the bytes we just received.
  273. if not self._handshakeDone:
  274. self._checkHandshakeStatus()
  275. # If the handshake still isn't finished, then we've nothing left to
  276. # do.
  277. if not self._handshakeDone:
  278. return
  279. # If we've any pending writes, this read may have un-blocked them, so
  280. # attempt to unbuffer them into the OpenSSL layer.
  281. if self._appSendBuffer:
  282. self._unbufferPendingWrites()
  283. # Since the handshake is complete, the wire-level bytes we just
  284. # processed might turn into some application-level bytes; try to pull
  285. # those out.
  286. self._flushReceiveBIO()
  287. def _shutdownTLS(self):
  288. """
  289. Initiate, or reply to, the shutdown handshake of the TLS layer.
  290. """
  291. try:
  292. shutdownSuccess = self._tlsConnection.shutdown()
  293. except Error:
  294. # Mid-handshake, a call to shutdown() can result in a
  295. # WantWantReadError, or rather an SSL_ERR_WANT_READ; but pyOpenSSL
  296. # doesn't allow us to get at the error. See:
  297. # https://github.com/pyca/pyopenssl/issues/91
  298. shutdownSuccess = False
  299. self._flushSendBIO()
  300. if shutdownSuccess:
  301. # Both sides have shutdown, so we can start closing lower-level
  302. # transport. This will also happen if we haven't started
  303. # negotiation at all yet, in which case shutdown succeeds
  304. # immediately.
  305. self.transport.loseConnection()
  306. def _tlsShutdownFinished(self, reason):
  307. """
  308. Called when TLS connection has gone away; tell underlying transport to
  309. disconnect.
  310. @param reason: a L{Failure} whose value is an L{Exception} if we want to
  311. report that failure through to the wrapped protocol's
  312. C{connectionLost}, or L{None} if the C{reason} that
  313. C{connectionLost} should receive should be coming from the
  314. underlying transport.
  315. @type reason: L{Failure} or L{None}
  316. """
  317. if reason is not None:
  318. # Squash an EOF in violation of the TLS protocol into
  319. # ConnectionLost, so that applications which might run over
  320. # multiple protocols can recognize its type.
  321. if _representsEOF(reason.value):
  322. reason = Failure(CONNECTION_LOST)
  323. if self._reason is None:
  324. self._reason = reason
  325. self._lostTLSConnection = True
  326. # We may need to send a TLS alert regarding the nature of the shutdown
  327. # here (for example, why a handshake failed), so always flush our send
  328. # buffer before telling our lower-level transport to go away.
  329. self._flushSendBIO()
  330. # Using loseConnection causes the application protocol's
  331. # connectionLost method to be invoked non-reentrantly, which is always
  332. # a nice feature. However, for error cases (reason != None) we might
  333. # want to use abortConnection when it becomes available. The
  334. # loseConnection call is basically tested by test_handshakeFailure.
  335. # At least one side will need to do it or the test never finishes.
  336. self.transport.loseConnection()
  337. def connectionLost(self, reason):
  338. """
  339. Handle the possible repetition of calls to this method (due to either
  340. the underlying transport going away or due to an error at the TLS
  341. layer) and make sure the base implementation only gets invoked once.
  342. """
  343. if not self._lostTLSConnection:
  344. # Tell the TLS connection that it's not going to get any more data
  345. # and give it a chance to finish reading.
  346. self._tlsConnection.bio_shutdown()
  347. self._flushReceiveBIO()
  348. self._lostTLSConnection = True
  349. reason = self._reason or reason
  350. self._reason = None
  351. self.connected = False
  352. ProtocolWrapper.connectionLost(self, reason)
  353. # Breaking reference cycle between self._tlsConnection and self.
  354. self._tlsConnection = None
  355. def loseConnection(self):
  356. """
  357. Send a TLS close alert and close the underlying connection.
  358. """
  359. if self.disconnecting or not self.connected:
  360. return
  361. # If connection setup has not finished, OpenSSL 1.0.2f+ will not shut
  362. # down the connection until we write some data to the connection which
  363. # allows the handshake to complete. However, since no data should be
  364. # written after loseConnection, this means we'll be stuck forever
  365. # waiting for shutdown to complete. Instead, we simply abort the
  366. # connection without trying to shut down cleanly:
  367. if not self._handshakeDone and not self._appSendBuffer:
  368. self.abortConnection()
  369. self.disconnecting = True
  370. if not self._appSendBuffer and self._producer is None:
  371. self._shutdownTLS()
  372. def abortConnection(self):
  373. """
  374. Tear down TLS state so that if the connection is aborted mid-handshake
  375. we don't deliver any further data from the application.
  376. """
  377. self._aborted = True
  378. self.disconnecting = True
  379. self._shutdownTLS()
  380. self.transport.abortConnection()
  381. def failVerification(self, reason):
  382. """
  383. Abort the connection during connection setup, giving a reason that
  384. certificate verification failed.
  385. @param reason: The reason that the verification failed; reported to the
  386. application protocol's C{connectionLost} method.
  387. @type reason: L{Failure}
  388. """
  389. self._reason = reason
  390. self.abortConnection()
  391. def write(self, bytes):
  392. """
  393. Process the given application bytes and send any resulting TLS traffic
  394. which arrives in the send BIO.
  395. If C{loseConnection} was called, subsequent calls to C{write} will
  396. drop the bytes on the floor.
  397. """
  398. # Writes after loseConnection are not supported, unless a producer has
  399. # been registered, in which case writes can happen until the producer
  400. # is unregistered:
  401. if self.disconnecting and self._producer is None:
  402. return
  403. self._write(bytes)
  404. def _bufferedWrite(self, octets):
  405. """
  406. Put the given octets into L{TLSMemoryBIOProtocol._appSendBuffer}, and
  407. tell any listening producer that it should pause because we are now
  408. buffering.
  409. """
  410. self._appSendBuffer.append(octets)
  411. if self._producer is not None:
  412. self._producer.pauseProducing()
  413. def _unbufferPendingWrites(self):
  414. """
  415. Un-buffer all waiting writes in L{TLSMemoryBIOProtocol._appSendBuffer}.
  416. """
  417. pendingWrites, self._appSendBuffer = self._appSendBuffer, []
  418. for eachWrite in pendingWrites:
  419. self._write(eachWrite)
  420. if self._appSendBuffer:
  421. # If OpenSSL ran out of buffer space in the Connection on our way
  422. # through the loop earlier and re-buffered any of our outgoing
  423. # writes, then we're done; don't consider any future work.
  424. return
  425. if self._producer is not None:
  426. # If we have a registered producer, let it know that we have some
  427. # more buffer space.
  428. self._producer.resumeProducing()
  429. return
  430. if self.disconnecting:
  431. # Finally, if we have no further buffered data, no producer wants
  432. # to send us more data in the future, and the application told us
  433. # to end the stream, initiate a TLS shutdown.
  434. self._shutdownTLS()
  435. def _write(self, bytes):
  436. """
  437. Process the given application bytes and send any resulting TLS traffic
  438. which arrives in the send BIO.
  439. This may be called by C{dataReceived} with bytes that were buffered
  440. before C{loseConnection} was called, which is why this function
  441. doesn't check for disconnection but accepts the bytes regardless.
  442. """
  443. if self._lostTLSConnection:
  444. return
  445. # A TLS payload is 16kB max
  446. bufferSize = 2**14
  447. # How far into the input we've gotten so far
  448. alreadySent = 0
  449. while alreadySent < len(bytes):
  450. toSend = bytes[alreadySent : alreadySent + bufferSize]
  451. try:
  452. sent = self._tlsConnection.send(toSend)
  453. except WantReadError:
  454. self._bufferedWrite(bytes[alreadySent:])
  455. break
  456. except Error:
  457. # Pretend TLS connection disconnected, which will trigger
  458. # disconnect of underlying transport. The error will be passed
  459. # to the application protocol's connectionLost method. The
  460. # other SSL implementation doesn't, but losing helpful
  461. # debugging information is a bad idea.
  462. self._tlsShutdownFinished(Failure())
  463. break
  464. else:
  465. # We've successfully handed off the bytes to the OpenSSL
  466. # Connection object.
  467. alreadySent += sent
  468. # See if OpenSSL wants to hand any bytes off to the underlying
  469. # transport as a result.
  470. self._flushSendBIO()
  471. def writeSequence(self, iovec):
  472. """
  473. Write a sequence of application bytes by joining them into one string
  474. and passing them to L{write}.
  475. """
  476. self.write(b"".join(iovec))
  477. def getPeerCertificate(self):
  478. return self._tlsConnection.get_peer_certificate()
  479. @property
  480. def negotiatedProtocol(self):
  481. """
  482. @see: L{INegotiated.negotiatedProtocol}
  483. """
  484. protocolName = None
  485. try:
  486. # If ALPN is not implemented that's ok, NPN might be.
  487. protocolName = self._tlsConnection.get_alpn_proto_negotiated()
  488. except (NotImplementedError, AttributeError):
  489. pass
  490. if protocolName not in (b"", None):
  491. # A protocol was selected using ALPN.
  492. return protocolName
  493. try:
  494. protocolName = self._tlsConnection.get_next_proto_negotiated()
  495. except (NotImplementedError, AttributeError):
  496. pass
  497. if protocolName != b"":
  498. return protocolName
  499. return None
  500. def registerProducer(self, producer, streaming):
  501. # If we've already disconnected, nothing to do here:
  502. if self._lostTLSConnection:
  503. producer.stopProducing()
  504. return
  505. # If we received a non-streaming producer, wrap it so it becomes a
  506. # streaming producer:
  507. if not streaming:
  508. producer = streamingProducer = _PullToPush(producer, self)
  509. producer = _ProducerMembrane(producer)
  510. # This will raise an exception if a producer is already registered:
  511. self.transport.registerProducer(producer, True)
  512. self._producer = producer
  513. # If we received a non-streaming producer, we need to start the
  514. # streaming wrapper:
  515. if not streaming:
  516. streamingProducer.startStreaming()
  517. def unregisterProducer(self):
  518. # If we have no producer, we don't need to do anything here.
  519. if self._producer is None:
  520. return
  521. # If we received a non-streaming producer, we need to stop the
  522. # streaming wrapper:
  523. if isinstance(self._producer._producer, _PullToPush):
  524. self._producer._producer.stopStreaming()
  525. self._producer = None
  526. self._producerPaused = False
  527. self.transport.unregisterProducer()
  528. if self.disconnecting and not self._appSendBuffer:
  529. self._shutdownTLS()
  530. @implementer(IOpenSSLClientConnectionCreator, IOpenSSLServerConnectionCreator)
  531. class _ContextFactoryToConnectionFactory:
  532. """
  533. Adapter wrapping a L{twisted.internet.interfaces.IOpenSSLContextFactory}
  534. into a L{IOpenSSLClientConnectionCreator} or
  535. L{IOpenSSLServerConnectionCreator}.
  536. See U{https://twistedmatrix.com/trac/ticket/7215} for work that should make
  537. this unnecessary.
  538. """
  539. def __init__(self, oldStyleContextFactory):
  540. """
  541. Construct a L{_ContextFactoryToConnectionFactory} with a
  542. L{twisted.internet.interfaces.IOpenSSLContextFactory}.
  543. Immediately call C{getContext} on C{oldStyleContextFactory} in order to
  544. force advance parameter checking, since old-style context factories
  545. don't actually check that their arguments to L{OpenSSL} are correct.
  546. @param oldStyleContextFactory: A factory that can produce contexts.
  547. @type oldStyleContextFactory:
  548. L{twisted.internet.interfaces.IOpenSSLContextFactory}
  549. """
  550. oldStyleContextFactory.getContext()
  551. self._oldStyleContextFactory = oldStyleContextFactory
  552. def _connectionForTLS(self, protocol):
  553. """
  554. Create an L{OpenSSL.SSL.Connection} object.
  555. @param protocol: The protocol initiating a TLS connection.
  556. @type protocol: L{TLSMemoryBIOProtocol}
  557. @return: a connection
  558. @rtype: L{OpenSSL.SSL.Connection}
  559. """
  560. context = self._oldStyleContextFactory.getContext()
  561. return Connection(context, None)
  562. def serverConnectionForTLS(self, protocol):
  563. """
  564. Construct an OpenSSL server connection from the wrapped old-style
  565. context factory.
  566. @note: Since old-style context factories don't distinguish between
  567. clients and servers, this is exactly the same as
  568. L{_ContextFactoryToConnectionFactory.clientConnectionForTLS}.
  569. @param protocol: The protocol initiating a TLS connection.
  570. @type protocol: L{TLSMemoryBIOProtocol}
  571. @return: a connection
  572. @rtype: L{OpenSSL.SSL.Connection}
  573. """
  574. return self._connectionForTLS(protocol)
  575. def clientConnectionForTLS(self, protocol):
  576. """
  577. Construct an OpenSSL server connection from the wrapped old-style
  578. context factory.
  579. @note: Since old-style context factories don't distinguish between
  580. clients and servers, this is exactly the same as
  581. L{_ContextFactoryToConnectionFactory.serverConnectionForTLS}.
  582. @param protocol: The protocol initiating a TLS connection.
  583. @type protocol: L{TLSMemoryBIOProtocol}
  584. @return: a connection
  585. @rtype: L{OpenSSL.SSL.Connection}
  586. """
  587. return self._connectionForTLS(protocol)
  588. class _AggregateSmallWrites:
  589. """
  590. Aggregate small writes so they get written in large batches.
  591. If this is used as part of a transport, the transport needs to call
  592. ``flush()`` immediately when ``loseConnection()`` is called, otherwise any
  593. buffered writes will never get written.
  594. @cvar MAX_BUFFER_SIZE: The maximum amount of bytes to buffer before writing
  595. them out.
  596. """
  597. MAX_BUFFER_SIZE = 64_000
  598. def __init__(self, write: Callable[[bytes], object], clock: IReactorTime):
  599. self._write = write
  600. self._clock = clock
  601. self._buffer: list[bytes] = []
  602. self._bufferLeft = self.MAX_BUFFER_SIZE
  603. self._scheduled: Optional[IDelayedCall] = None
  604. def write(self, data: bytes) -> None:
  605. """
  606. Buffer the data, or write it immediately if we've accumulated enough to
  607. make it worth it.
  608. Accumulating too much data can result in higher memory usage.
  609. """
  610. self._buffer.append(data)
  611. self._bufferLeft -= len(data)
  612. if self._bufferLeft < 0:
  613. # We've accumulated enough we should just write it out. No need to
  614. # schedule a flush, since we just flushed everything.
  615. self.flush()
  616. return
  617. if self._scheduled:
  618. # We already have a scheduled send, so with the data in the buffer,
  619. # there is nothing more to do here.
  620. return
  621. # Schedule the write of the accumulated buffer for the next reactor
  622. # iteration.
  623. self._scheduled = self._clock.callLater(0, self._scheduledFlush)
  624. def _scheduledFlush(self) -> None:
  625. """Called in next reactor iteration."""
  626. self._scheduled = None
  627. self.flush()
  628. def flush(self) -> None:
  629. """Flush any buffered writes."""
  630. if self._buffer:
  631. self._bufferLeft = self.MAX_BUFFER_SIZE
  632. self._write(b"".join(self._buffer))
  633. del self._buffer[:]
  634. def _get_default_clock() -> IReactorTime:
  635. """
  636. Return the default reactor.
  637. This is a function so it can be monkey-patched in tests, specifically
  638. L{twisted.web.test.test_agent}.
  639. """
  640. from twisted.internet import reactor
  641. return cast(IReactorTime, reactor)
  642. class BufferingTLSTransport(TLSMemoryBIOProtocol):
  643. """
  644. A TLS transport implemented by wrapping buffering around a
  645. L{TLSMemoryBIOProtocol}.
  646. Doing many small writes directly to a L{OpenSSL.SSL.Connection}, as
  647. implemented in L{TLSMemoryBIOProtocol}, can add significant CPU and
  648. bandwidth overhead. Thus, even when writing is possible, small writes will
  649. get aggregated and written as a single write at the next reactor iteration.
  650. """
  651. # Implementation Note: An implementation based on composition would be
  652. # nicer, but there's close integration between L{ProtocolWrapper}
  653. # subclasses like L{TLSMemoryBIOProtocol} and the corresponding factory. An
  654. # attempt to implement this with broke things like
  655. # L{TLSMemoryBIOFactory.protocols} having the correct instances, whereas
  656. # subclassing makes that work.
  657. def __init__(
  658. self,
  659. factory: TLSMemoryBIOFactory,
  660. wrappedProtocol: IProtocol,
  661. _connectWrapped: bool = True,
  662. ):
  663. super().__init__(factory, wrappedProtocol, _connectWrapped)
  664. actual_write = super().write
  665. self._aggregator = _AggregateSmallWrites(actual_write, factory._clock)
  666. # This is kinda ugly, but speeds things up a lot in a hot path with
  667. # lots of small TLS writes. May become unnecessary in Python 3.13 or
  668. # later if JIT and/or inlining becomes a thing.
  669. self.write = self._aggregator.write # type: ignore[method-assign]
  670. def writeSequence(self, sequence: Iterable[bytes]) -> None:
  671. self._aggregator.write(b"".join(sequence))
  672. def loseConnection(self) -> None:
  673. self._aggregator.flush()
  674. super().loseConnection()
  675. class TLSMemoryBIOFactory(WrappingFactory):
  676. """
  677. L{TLSMemoryBIOFactory} adds TLS to connections.
  678. @ivar _creatorInterface: the interface which L{_connectionCreator} is
  679. expected to implement.
  680. @type _creatorInterface: L{zope.interface.interfaces.IInterface}
  681. @ivar _connectionCreator: a callable which creates an OpenSSL Connection
  682. object.
  683. @type _connectionCreator: 1-argument callable taking
  684. L{TLSMemoryBIOProtocol} and returning L{OpenSSL.SSL.Connection}.
  685. """
  686. protocol = BufferingTLSTransport
  687. noisy = False # disable unnecessary logging.
  688. def __init__(
  689. self,
  690. contextFactory,
  691. isClient,
  692. wrappedFactory,
  693. clock=None,
  694. ):
  695. """
  696. Create a L{TLSMemoryBIOFactory}.
  697. @param contextFactory: Configuration parameters used to create an
  698. OpenSSL connection. In order of preference, what you should pass
  699. here should be:
  700. 1. L{twisted.internet.ssl.CertificateOptions} (if you're
  701. writing a server) or the result of
  702. L{twisted.internet.ssl.optionsForClientTLS} (if you're
  703. writing a client). If you want security you should really
  704. use one of these.
  705. 2. If you really want to implement something yourself, supply a
  706. provider of L{IOpenSSLClientConnectionCreator} or
  707. L{IOpenSSLServerConnectionCreator}.
  708. 3. If you really have to, supply a
  709. L{twisted.internet.ssl.ContextFactory}. This will likely be
  710. deprecated at some point so please upgrade to the new
  711. interfaces.
  712. @type contextFactory: L{IOpenSSLClientConnectionCreator} or
  713. L{IOpenSSLServerConnectionCreator}, or, for compatibility with
  714. older code, anything implementing
  715. L{twisted.internet.interfaces.IOpenSSLContextFactory}. See
  716. U{https://twistedmatrix.com/trac/ticket/7215} for information on
  717. the upcoming deprecation of passing a
  718. L{twisted.internet.ssl.ContextFactory} here.
  719. @param isClient: Is this a factory for TLS client connections; in other
  720. words, those that will send a C{ClientHello} greeting? L{True} if
  721. so, L{False} otherwise. This flag determines what interface is
  722. expected of C{contextFactory}. If L{True}, C{contextFactory}
  723. should provide L{IOpenSSLClientConnectionCreator}; otherwise it
  724. should provide L{IOpenSSLServerConnectionCreator}.
  725. @type isClient: L{bool}
  726. @param wrappedFactory: A factory which will create the
  727. application-level protocol.
  728. @type wrappedFactory: L{twisted.internet.interfaces.IProtocolFactory}
  729. """
  730. WrappingFactory.__init__(self, wrappedFactory)
  731. if isClient:
  732. creatorInterface = IOpenSSLClientConnectionCreator
  733. else:
  734. creatorInterface = IOpenSSLServerConnectionCreator
  735. self._creatorInterface = creatorInterface
  736. if not creatorInterface.providedBy(contextFactory):
  737. contextFactory = _ContextFactoryToConnectionFactory(contextFactory)
  738. self._connectionCreator = contextFactory
  739. if clock is None:
  740. clock = _get_default_clock()
  741. self._clock = clock
  742. def logPrefix(self):
  743. """
  744. Annotate the wrapped factory's log prefix with some text indicating TLS
  745. is in use.
  746. @rtype: C{str}
  747. """
  748. if ILoggingContext.providedBy(self.wrappedFactory):
  749. logPrefix = self.wrappedFactory.logPrefix()
  750. else:
  751. logPrefix = self.wrappedFactory.__class__.__name__
  752. return f"{logPrefix} (TLS)"
  753. def _applyProtocolNegotiation(self, connection):
  754. """
  755. Applies ALPN/NPN protocol neogitation to the connection, if the factory
  756. supports it.
  757. @param connection: The OpenSSL connection object to have ALPN/NPN added
  758. to it.
  759. @type connection: L{OpenSSL.SSL.Connection}
  760. @return: Nothing
  761. @rtype: L{None}
  762. """
  763. if IProtocolNegotiationFactory.providedBy(self.wrappedFactory):
  764. protocols = self.wrappedFactory.acceptableProtocols()
  765. context = connection.get_context()
  766. _setAcceptableProtocols(context, protocols)
  767. return
  768. def _createConnection(self, tlsProtocol):
  769. """
  770. Create an OpenSSL connection and set it up good.
  771. @param tlsProtocol: The protocol which is establishing the connection.
  772. @type tlsProtocol: L{TLSMemoryBIOProtocol}
  773. @return: an OpenSSL connection object for C{tlsProtocol} to use
  774. @rtype: L{OpenSSL.SSL.Connection}
  775. """
  776. connectionCreator = self._connectionCreator
  777. if self._creatorInterface is IOpenSSLClientConnectionCreator:
  778. connection = connectionCreator.clientConnectionForTLS(tlsProtocol)
  779. self._applyProtocolNegotiation(connection)
  780. connection.set_connect_state()
  781. else:
  782. connection = connectionCreator.serverConnectionForTLS(tlsProtocol)
  783. self._applyProtocolNegotiation(connection)
  784. connection.set_accept_state()
  785. return connection