31
31
32
32
from twisted .internet .defer import Deferred
33
33
from twisted .internet .address import IPv4Address , UNIXAddress
34
- from twisted .internet .interfaces import ITransport , IProcessTransport , ISSLTransport
34
+ from twisted .internet .interfaces import ITransport , IProcessTransport
35
35
36
36
from autobahn .wamp .types import TransportDetails
37
37
48
48
_HAS_IPV6 = False
49
49
IPv6Address = type (None )
50
50
51
+ try :
52
+ from twisted .internet .interfaces import ISSLTransport
53
+ from twisted .protocols .tls import TLSMemoryBIOProtocol
54
+ from OpenSSL .SSL import Connection
55
+ _HAS_TLS = True
56
+ except ImportError :
57
+ _HAS_TLS = False
58
+
51
59
__all = (
52
60
'sleep' ,
53
61
'peer2str' ,
@@ -116,10 +124,7 @@ def peer2str(transport: Union[ITransport, IProcessTransport]) -> str:
116
124
return res
117
125
118
126
119
- try :
120
- from twisted .protocols .tls import TLSMemoryBIOProtocol
121
- from OpenSSL .SSL import Connection
122
- except ImportError :
127
+ if not _HAS_TLS :
123
128
def transport_channel_id (transport : object , is_server : bool , channel_id_type : Optional [str ] = None ) -> Optional [bytes ]:
124
129
if channel_id_type is None :
125
130
return b'\x00 ' * 32
@@ -189,64 +194,78 @@ def transport_channel_id(transport: object, is_server: bool, channel_id_type: Op
189
194
raise NotImplementedError ('should not arrive here (unhandled channel_id_type "{}")' .format (channel_id_type ))
190
195
191
196
192
- def extract_peer_certificate (transport : TLSMemoryBIOProtocol ) -> Optional [Dict [str , Any ]]:
193
- """
194
- Extract TLS x509 client certificate information from a Twisted stream transport, and
195
- return a dict with x509 TLS client certificate information (if the client provided a
196
- TLS client certificate).
197
- """
198
- # check if the Twisted transport is a TLSMemoryBIOProtocol
199
- if not (ISSLTransport .providedBy (transport ) and hasattr (transport , 'getPeerCertificate' )):
200
- return None
197
+ if not _HAS_TLS :
198
+ def extract_peer_certificate (transport : object ) -> Optional [Dict [str , Any ]]:
199
+ """
200
+ Dummy when no TLS is available.
201
201
202
- cert = transport .getPeerCertificate ()
203
- if cert :
204
- # extract x509 name components from an OpenSSL X509Name object
205
- def maybe_bytes (_value ):
206
- if isinstance (_value , bytes ):
207
- return _value .decode ('utf8' )
208
- else :
209
- return _value
210
-
211
- result = {
212
- 'md5' : '{}' .format (maybe_bytes (cert .digest ('md5' ))).upper (),
213
- 'sha1' : '{}' .format (maybe_bytes (cert .digest ('sha1' ))).upper (),
214
- 'sha256' : '{}' .format (maybe_bytes (cert .digest ('sha256' ))).upper (),
215
- 'expired' : bool (cert .has_expired ()),
216
- 'hash' : maybe_bytes (cert .subject_name_hash ()),
217
- 'serial' : int (cert .get_serial_number ()),
218
- 'signature_algorithm' : maybe_bytes (cert .get_signature_algorithm ()),
219
- 'version' : int (cert .get_version ()),
220
- 'not_before' : maybe_bytes (cert .get_notBefore ()),
221
- 'not_after' : maybe_bytes (cert .get_notAfter ()),
222
- 'extensions' : []
223
- }
202
+ :param transport: Ignored.
203
+ :return: Always return ``None``.
204
+ """
205
+ return None
206
+ else :
207
+ def extract_peer_certificate (transport : TLSMemoryBIOProtocol ) -> Optional [Dict [str , Any ]]:
208
+ """
209
+ Extract TLS x509 client certificate information from a Twisted stream transport, and
210
+ return a dict with x509 TLS client certificate information (if the client provided a
211
+ TLS client certificate).
224
212
225
- for i in range (cert .get_extension_count ()):
226
- ext = cert .get_extension (i )
227
- ext_info = {
228
- 'name' : '{}' .format (maybe_bytes (ext .get_short_name ())),
229
- 'value' : '{}' .format (maybe_bytes (ext )),
230
- 'critical' : ext .get_critical () != 0
213
+ :param transport: The secure transport from which to extract the peer certificate (if present).
214
+ :returns: If the peer provided a certificate, the parsed certificate information set.
215
+ """
216
+ # check if the Twisted transport is a TLSMemoryBIOProtocol
217
+ if not (ISSLTransport .providedBy (transport ) and hasattr (transport , 'getPeerCertificate' )):
218
+ return None
219
+
220
+ cert = transport .getPeerCertificate ()
221
+ if cert :
222
+ # extract x509 name components from an OpenSSL X509Name object
223
+ def maybe_bytes (_value ):
224
+ if isinstance (_value , bytes ):
225
+ return _value .decode ('utf8' )
226
+ else :
227
+ return _value
228
+
229
+ result = {
230
+ 'md5' : '{}' .format (maybe_bytes (cert .digest ('md5' ))).upper (),
231
+ 'sha1' : '{}' .format (maybe_bytes (cert .digest ('sha1' ))).upper (),
232
+ 'sha256' : '{}' .format (maybe_bytes (cert .digest ('sha256' ))).upper (),
233
+ 'expired' : bool (cert .has_expired ()),
234
+ 'hash' : maybe_bytes (cert .subject_name_hash ()),
235
+ 'serial' : int (cert .get_serial_number ()),
236
+ 'signature_algorithm' : maybe_bytes (cert .get_signature_algorithm ()),
237
+ 'version' : int (cert .get_version ()),
238
+ 'not_before' : maybe_bytes (cert .get_notBefore ()),
239
+ 'not_after' : maybe_bytes (cert .get_notAfter ()),
240
+ 'extensions' : []
231
241
}
232
- result ['extensions' ].append (ext_info )
233
242
234
- for entity , name in [('subject' , cert .get_subject ()), ('issuer' , cert .get_issuer ())]:
235
- result [entity ] = {}
236
- for key , value in name .get_components ():
237
- key = maybe_bytes (key )
238
- value = maybe_bytes (value )
239
- result [entity ]['{}' .format (key ).lower ()] = '{}' .format (value )
243
+ for i in range (cert .get_extension_count ()):
244
+ ext = cert .get_extension (i )
245
+ ext_info = {
246
+ 'name' : '{}' .format (maybe_bytes (ext .get_short_name ())),
247
+ 'value' : '{}' .format (maybe_bytes (ext )),
248
+ 'critical' : ext .get_critical () != 0
249
+ }
250
+ result ['extensions' ].append (ext_info )
240
251
241
- return result
252
+ for entity , name in [('subject' , cert .get_subject ()), ('issuer' , cert .get_issuer ())]:
253
+ result [entity ] = {}
254
+ for key , value in name .get_components ():
255
+ key = maybe_bytes (key )
256
+ value = maybe_bytes (value )
257
+ result [entity ]['{}' .format (key ).lower ()] = '{}' .format (value )
258
+
259
+ return result
242
260
243
261
244
262
def create_transport_details (transport : Union [ITransport , IProcessTransport ], is_server : bool ) -> TransportDetails :
245
263
"""
264
+ Create transport details from Twisted transport.
246
265
247
- :param transport:
248
- :param is_server:
249
- :return:
266
+ :param transport: The Twisted transport to extract information from.
267
+ :param is_server: Flag indicating whether this transport side is a "server" (as in TCP server).
268
+ :return: Transport details object filled with information from the Twisted transport.
250
269
"""
251
270
peer = peer2str (transport )
252
271
@@ -259,17 +278,20 @@ def create_transport_details(transport: Union[ITransport, IProcessTransport], is
259
278
own_tid = threading .get_ident ()
260
279
own_fd = - 1
261
280
262
- is_secure = ISSLTransport .providedBy (transport )
263
- if is_secure :
281
+ if _HAS_TLS and ISSLTransport .providedBy (transport ):
264
282
channel_id = {
265
283
'tls-unique' : transport_channel_id (transport , is_server , 'tls-unique' ),
266
284
}
267
285
channel_type = TransportDetails .CHANNEL_TYPE_TLS
268
286
peer_cert = extract_peer_certificate (transport )
287
+ is_secure = True
269
288
else :
270
289
channel_id = {}
271
290
channel_type = TransportDetails .CHANNEL_TYPE_TCP
272
291
peer_cert = None
292
+ is_secure = False
293
+
294
+ # FIXME: really set a default (websocket)?
273
295
channel_framing = TransportDetails .CHANNEL_FRAMING_WEBSOCKET
274
296
275
297
return TransportDetails (channel_type = channel_type , channel_framing = channel_framing , peer = peer ,
0 commit comments