| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: twisted.test.test_ssl -*- | |
| 2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories. | |
| 3 # See LICENSE for details. | |
| 4 | |
| 5 | |
| 6 """ | |
| 7 SSL transport. Requires PyOpenSSL (http://pyopenssl.sf.net). | |
| 8 | |
| 9 SSL connections require a ContextFactory so they can create SSL contexts. | |
| 10 End users should only use the ContextFactory classes directly - for SSL | |
| 11 connections use the reactor.connectSSL/listenSSL and so on, as documented | |
| 12 in IReactorSSL. | |
| 13 | |
| 14 All server context factories should inherit from ContextFactory, and all | |
| 15 client context factories should inherit from ClientContextFactory. At the | |
| 16 moment this is not enforced, but in the future it might be. | |
| 17 | |
| 18 Future Plans: | |
| 19 - split module so reactor-specific classes are in a separate module | |
| 20 - support for switching TCP into SSL | |
| 21 - more options | |
| 22 | |
| 23 Maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>} | |
| 24 """ | |
| 25 | |
| 26 # If something goes wrong, most notably an OpenSSL import failure, | |
| 27 # sys.modules['twisted.internet.ssl'] will be bound to a partially | |
| 28 # initialized module object. This is wacko, but we will take advantage | |
| 29 # of it to publish whether or not SSL is available. | |
| 30 # See the end of this module for the other half of this solution. | |
| 31 | |
| 32 # The correct idiom to import this module is thus: | |
| 33 | |
| 34 # try: | |
| 35 # from twisted.internet import ssl | |
| 36 # except ImportError: | |
| 37 # # happens the first time the interpreter tries to import it | |
| 38 # ssl = None | |
| 39 # if ssl and not ssl.supported: | |
| 40 # # happens second and later times | |
| 41 # ssl = None | |
| 42 | |
| 43 supported = False | |
| 44 | |
| 45 # System imports | |
| 46 from OpenSSL import SSL | |
| 47 from zope.interface import implements, implementsOnly, implementedBy | |
| 48 | |
| 49 # sibling imports | |
| 50 import tcp, interfaces | |
| 51 | |
| 52 # Twisted imports | |
| 53 from twisted.internet import base, address | |
| 54 | |
| 55 | |
| 56 class ContextFactory: | |
| 57 """A factory for SSL context objects, for server SSL connections.""" | |
| 58 | |
| 59 isClient = 0 | |
| 60 | |
| 61 def getContext(self): | |
| 62 """Return a SSL.Context object. override in subclasses.""" | |
| 63 raise NotImplementedError | |
| 64 | |
| 65 | |
| 66 class DefaultOpenSSLContextFactory(ContextFactory): | |
| 67 | |
| 68 def __init__(self, privateKeyFileName, certificateFileName, | |
| 69 sslmethod=SSL.SSLv23_METHOD): | |
| 70 """ | |
| 71 @param privateKeyFileName: Name of a file containing a private key | |
| 72 @param certificateFileName: Name of a file containing a certificate | |
| 73 @param sslmethod: The SSL method to use | |
| 74 """ | |
| 75 self.privateKeyFileName = privateKeyFileName | |
| 76 self.certificateFileName = certificateFileName | |
| 77 self.sslmethod = sslmethod | |
| 78 self.cacheContext() | |
| 79 | |
| 80 def cacheContext(self): | |
| 81 ctx = SSL.Context(self.sslmethod) | |
| 82 ctx.use_certificate_file(self.certificateFileName) | |
| 83 ctx.use_privatekey_file(self.privateKeyFileName) | |
| 84 self._context = ctx | |
| 85 | |
| 86 def __getstate__(self): | |
| 87 d = self.__dict__.copy() | |
| 88 del d['_context'] | |
| 89 return d | |
| 90 | |
| 91 def __setstate__(self, state): | |
| 92 self.__dict__ = state | |
| 93 self.cacheContext() | |
| 94 | |
| 95 def getContext(self): | |
| 96 """Create an SSL context. | |
| 97 """ | |
| 98 return self._context | |
| 99 | |
| 100 | |
| 101 class ClientContextFactory: | |
| 102 """A context factory for SSL clients.""" | |
| 103 | |
| 104 isClient = 1 | |
| 105 method = SSL.SSLv3_METHOD | |
| 106 | |
| 107 def getContext(self): | |
| 108 return SSL.Context(self.method) | |
| 109 | |
| 110 | |
| 111 class Client(tcp.Client): | |
| 112 """I am an SSL client.""" | |
| 113 | |
| 114 implementsOnly(interfaces.ISSLTransport, | |
| 115 *[i for i in implementedBy(tcp.Client) if i != interfaces.ITL
STransport]) | |
| 116 | |
| 117 def __init__(self, host, port, bindAddress, ctxFactory, connector, reactor=N
one): | |
| 118 # tcp.Client.__init__ depends on self.ctxFactory being set | |
| 119 self.ctxFactory = ctxFactory | |
| 120 tcp.Client.__init__(self, host, port, bindAddress, connector, reactor) | |
| 121 | |
| 122 def getHost(self): | |
| 123 """Returns the address from which I am connecting.""" | |
| 124 h, p = self.socket.getsockname() | |
| 125 return address.IPv4Address('TCP', h, p, 'SSL') | |
| 126 | |
| 127 def getPeer(self): | |
| 128 """Returns the address that I am connected.""" | |
| 129 return address.IPv4Address('TCP', self.addr[0], self.addr[1], 'SSL') | |
| 130 | |
| 131 def _connectDone(self): | |
| 132 self.startTLS(self.ctxFactory) | |
| 133 self.startWriting() | |
| 134 tcp.Client._connectDone(self) | |
| 135 | |
| 136 | |
| 137 class Server(tcp.Server): | |
| 138 """I am an SSL server. | |
| 139 """ | |
| 140 | |
| 141 implements(interfaces.ISSLTransport) | |
| 142 | |
| 143 def getHost(self): | |
| 144 """Return server's address.""" | |
| 145 h, p = self.socket.getsockname() | |
| 146 return address.IPv4Address('TCP', h, p, 'SSL') | |
| 147 | |
| 148 def getPeer(self): | |
| 149 """Return address of peer.""" | |
| 150 h, p = self.client | |
| 151 return address.IPv4Address('TCP', h, p, 'SSL') | |
| 152 | |
| 153 | |
| 154 class Port(tcp.Port): | |
| 155 """I am an SSL port.""" | |
| 156 _socketShutdownMethod = 'sock_shutdown' | |
| 157 | |
| 158 transport = Server | |
| 159 | |
| 160 def __init__(self, port, factory, ctxFactory, backlog=50, interface='', reac
tor=None): | |
| 161 tcp.Port.__init__(self, port, factory, backlog, interface, reactor) | |
| 162 self.ctxFactory = ctxFactory | |
| 163 | |
| 164 def createInternetSocket(self): | |
| 165 """(internal) create an SSL socket | |
| 166 """ | |
| 167 sock = tcp.Port.createInternetSocket(self) | |
| 168 return SSL.Connection(self.ctxFactory.getContext(), sock) | |
| 169 | |
| 170 def _preMakeConnection(self, transport): | |
| 171 # *Don't* call startTLS here | |
| 172 # The transport already has the SSL.Connection object from above | |
| 173 transport._startTLS() | |
| 174 return tcp.Port._preMakeConnection(self, transport) | |
| 175 | |
| 176 | |
| 177 class Connector(base.BaseConnector): | |
| 178 def __init__(self, host, port, factory, contextFactory, timeout, bindAddress
, reactor=None): | |
| 179 self.host = host | |
| 180 self.port = port | |
| 181 self.bindAddress = bindAddress | |
| 182 self.contextFactory = contextFactory | |
| 183 base.BaseConnector.__init__(self, factory, timeout, reactor) | |
| 184 | |
| 185 def _makeTransport(self): | |
| 186 return Client(self.host, self.port, self.bindAddress, self.contextFactor
y, self, self.reactor) | |
| 187 | |
| 188 def getDestination(self): | |
| 189 return address.IPv4Address('TCP', self.host, self.port, 'SSL') | |
| 190 | |
| 191 from twisted.internet._sslverify import DistinguishedName, DN, Certificate | |
| 192 from twisted.internet._sslverify import CertificateRequest, PrivateCertificate | |
| 193 from twisted.internet._sslverify import KeyPair | |
| 194 from twisted.internet._sslverify import OpenSSLCertificateOptions as Certificate
Options | |
| 195 | |
| 196 __all__ = [ | |
| 197 "ContextFactory", "DefaultOpenSSLContextFactory", "ClientContextFactory", | |
| 198 | |
| 199 'DistinguishedName', 'DN', | |
| 200 'Certificate', 'CertificateRequest', 'PrivateCertificate', | |
| 201 'KeyPair', | |
| 202 'CertificateOptions', | |
| 203 ] | |
| 204 | |
| 205 supported = True | |
| OLD | NEW |