7
7
8
8
Environment variables used:
9
9
SESSION_PS_SIZE
10
+ SESSION_SECLEVEL
11
+ SESSION_SSL_VERIFY
10
12
TNZ_COLORS
11
13
TNZ_LOGGING
12
14
ZTI_SECLEVEL
13
15
14
- Copyright 2021, 2023 IBM Inc. All Rights Reserved.
16
+ Copyright 2021, 2024 IBM Inc. All Rights Reserved.
15
17
16
18
SPDX-License-Identifier: Apache-2.0
17
19
"""
@@ -94,6 +96,8 @@ def __init__(self, name=None):
94
96
self .colors = 768
95
97
96
98
self .__secure = False
99
+ self .__cert_verified = False
100
+ self .__start_tls_hostname = None
97
101
self .__host_verified = False
98
102
self ._event = None
99
103
self .__loop = None
@@ -372,6 +376,12 @@ def connect(self, host=None, port=None,
372
376
373
377
if host is None :
374
378
host = "127.0.0.1" # default host
379
+ elif not secure : # if might need hostname later for start_tls
380
+ import socket
381
+ try :
382
+ self .__start_tls_hostname = socket .getfqdn (host )
383
+ except socket .gaierror :
384
+ pass
375
385
376
386
if port is None :
377
387
if secure is False :
@@ -383,13 +393,20 @@ def connect(self, host=None, port=None,
383
393
self ._event = event
384
394
385
395
self .__secure = False
396
+ self .__cert_verified = False
386
397
self .__host_verified = False
387
398
399
+ def _connection_made (_ , transport ):
400
+ self ._transport = transport
401
+ self .seslost = False
402
+ if context :
403
+ self .__secure = True
404
+ if context .verify_mode == ssl .CERT_REQUIRED :
405
+ self .__cert_verified = True
406
+ self .__host_verified = context .check_hostname
407
+
388
408
class _TnzProtocol (asyncio .BaseProtocol ):
389
- @staticmethod
390
- def connection_made (transport ):
391
- self ._transport = transport
392
- self .seslost = False
409
+ connection_made = _connection_made
393
410
394
411
@staticmethod
395
412
def connection_lost (exc ):
@@ -425,22 +442,7 @@ def resume_writing():
425
442
"""
426
443
self ._log_warn ("resume_writing" )
427
444
428
- if secure :
429
- context = ssl .SSLContext (ssl .PROTOCOL_TLS_CLIENT )
430
- if os .getenv ("ZTI_SECLEVEL" , "2" ) == "1" :
431
- context .set_ciphers ("DEFAULT@SECLEVEL=1" )
432
-
433
- if verifycert :
434
- context .load_verify_locations ("ibm-cacerts.pem" )
435
- self .__host_verified = True # ? too soon ?
436
-
437
- else :
438
- context .check_hostname = False # insecure FIXME
439
- context .verify_mode = ssl .CERT_NONE # insecure FIXME
440
-
441
- else :
442
- context = None
443
-
445
+ context = self .__create_context (verifycert ) if secure else None
444
446
coro = self .__connect (_TnzProtocol , host , port , context )
445
447
loop = self .__get_event_loop ()
446
448
task = loop .create_task (coro )
@@ -2293,9 +2295,7 @@ def _process(self, data):
2293
2295
2294
2296
elif data == b"\xff \xfa \x2e \x01 " : # IAC SB ...
2295
2297
self .__log_info ("i<< START_TLS FOLLOWS" )
2296
- context = ssl .SSLContext (ssl .PROTOCOL_TLS_CLIENT )
2297
- context .check_hostname = False
2298
- context .verify_mode = ssl .CERT_NONE
2298
+ context = self .__create_context ()
2299
2299
coro = self .__start_tls (context )
2300
2300
task = self .__loop .create_task (coro )
2301
2301
self .__connect_task = task
@@ -3713,6 +3713,37 @@ async def __connect(self, protocol, host, port, ssl_context):
3713
3713
if self .__connect_task is task :
3714
3714
self .__connect_task = None
3715
3715
3716
+ def __create_context (self , verifycert = None ):
3717
+ """Create an SSL context for a session.
3718
+
3719
+ Uses environment variables to determine context options.
3720
+ """
3721
+ context = ssl .create_default_context ()
3722
+
3723
+ getenv = os .environ .get
3724
+ seclevel = getenv ("SESSION_SECLEVEL" )
3725
+ if not seclevel and getenv ("ZTI_SECLEVEL" ) == "1" :
3726
+ seclevel = "1"
3727
+
3728
+ if seclevel :
3729
+ context .set_ciphers (f"DEFAULT@SECLEVEL={ seclevel } " )
3730
+
3731
+ ssl_verify = getenv ("SESSION_SSL_VERIFY" , "" )
3732
+ context .check_hostname = ssl_verify == "hostname"
3733
+
3734
+ if verifycert is None :
3735
+ if not ssl_verify and context .check_hostname :
3736
+ verifycert = True
3737
+ else :
3738
+ verifycert = ssl_verify in ("cert" , "hostname" )
3739
+
3740
+ if verifycert :
3741
+ context .verify_mode = ssl .CERT_REQUIRED
3742
+ else :
3743
+ context .verify_mode = ssl .CERT_NONE
3744
+
3745
+ return context
3746
+
3716
3747
def __erase (self , saddr , eaddr ):
3717
3748
"""Process erase function.
3718
3749
@@ -4447,10 +4478,19 @@ async def __start_tls(self, context):
4447
4478
transport = self ._transport
4448
4479
protocol = transport .get_protocol ()
4449
4480
self ._transport = None
4481
+ server_hostname = None
4482
+ if context .check_hostname :
4483
+ server_hostname = self .__start_tls_hostname
4484
+ if server_hostname is None :
4485
+ raise TnzError ("no hostname for check_hostname" )
4486
+
4450
4487
try :
4451
- transport = await loop .start_tls (transport ,
4452
- protocol ,
4453
- context )
4488
+ transport = await loop .start_tls (
4489
+ transport ,
4490
+ protocol ,
4491
+ context ,
4492
+ server_hostname = server_hostname ,
4493
+ )
4454
4494
except asyncio .CancelledError :
4455
4495
self .seslost = True
4456
4496
self ._event .set ()
@@ -4463,6 +4503,10 @@ async def __start_tls(self, context):
4463
4503
else :
4464
4504
self ._transport = transport
4465
4505
self .__secure = True
4506
+ if context .verify_mode == ssl .CERT_REQUIRED :
4507
+ self .__cert_verified = True
4508
+ self .__host_verified = context .check_hostname
4509
+
4466
4510
self .__log_debug ("__start_tls transport: %r" , transport )
4467
4511
self .send () # in case send() ignored for _transport = None
4468
4512
@@ -4702,6 +4746,12 @@ def __repl(mat):
4702
4746
4703
4747
# Readonly properties
4704
4748
4749
+ @property
4750
+ def cert_verified (self ):
4751
+ """Bool indicating if secure and cert was verified as trusted.
4752
+ """
4753
+ return self .__cert_verified
4754
+
4705
4755
@property
4706
4756
def host_verified (self ):
4707
4757
"""Bool indicating if secure and host was verified.
@@ -4871,15 +4921,16 @@ def connect(host=None, port=None,
4871
4921
secure = True for encrypted connection
4872
4922
verifycert only has meaning when secure is True
4873
4923
"""
4924
+ ssl_verify = os .environ .get ("SESSION_SSL_VERIFY" , "" )
4874
4925
tnz = Tnz (name = name )
4875
4926
4876
4927
if port is None and secure is not False :
4877
4928
port = 992
4878
- if verifycert is None :
4929
+ if verifycert is None and secure is None and not ssl_verify :
4879
4930
verifycert = False
4880
4931
4881
4932
if secure and verifycert is None :
4882
- verifycert = True
4933
+ verifycert = ssl_verify in ( "cert" , "hostname" )
4883
4934
4884
4935
if secure is None :
4885
4936
secure = bool (port != 23 )
0 commit comments