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 |