From jython-checkins at python.org Sun Jun 16 14:01:28 2019 From: jython-checkins at python.org (jeff.allen) Date: Sun, 16 Jun 2019 18:01:28 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_test=5Fssl_failures_from_w?= =?utf-8?q?eak_certificate=2E_Fixes_=232774=2E?= Message-ID: <20190616180128.1.B2161F2D254E4DF4@mg.python.org> https://hg.python.org/jython/rev/3d37bb31d66d changeset: 8250:3d37bb31d66d user: Adam Burke date: Sun Jun 16 17:53:43 2019 +0100 summary: test_ssl failures from weak certificate. Fixes #2774. The minimal fix is simply the update to selfsigned_pythontestdotnet.pem from Python core. See Python bug 36816. This change also merges from the latest 2.7 version of test_ssl, and separates most Jython variation into test_ssl_jy.py. This is to ease future maintenance and/or patches back into CPython. No new tests are skipped. Method test_algorithms() is deleted as per 2.7 and Python bug 25764, although it actually ran cleanly locally. files: Lib/ssl.py | 4 +- Lib/test/capath/efa5f9c3.0 | 34 + Lib/test/regrtest.py | 1 + Lib/test/selfsigned_pythontestdotnet.pem | 46 +- Lib/test/test_ssl.py | 717 ++++++--- Lib/test/test_ssl_jy.py | 231 +++ NEWS | 1 + lib-python/2.7/test/selfsigned_pythontestdotnet.pem | 34 + 8 files changed, 806 insertions(+), 262 deletions(-) diff --git a/Lib/ssl.py b/Lib/ssl.py --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -97,6 +97,8 @@ VERIFY_DEFAULT, VERIFY_CRL_CHECK_LEAF, VERIFY_CRL_CHECK_CHAIN, VERIFY_X509_STRICT = 0, 4, 12, 32 +HAS_TLSv1_3 = False + CHANNEL_BINDING_TYPES = [] # https://docs.python.org/2/library/ssl.html#ssl.HAS_ALPN etc... @@ -691,7 +693,7 @@ self._sock._handle_channel_future(self._handshake_future, "SSL handshake") def dup(self): - raise NotImplemented("Can't dup() %s instances" % + raise NotImplementedError("Can't dup() %s instances" % self.__class__.__name__) @raises_java_exception diff --git a/Lib/test/capath/efa5f9c3.0 b/Lib/test/capath/efa5f9c3.0 new file mode 100644 --- /dev/null +++ b/Lib/test/capath/efa5f9c3.0 @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9zCCA9+gAwIBAgIUH98b4Fw/DyugC9cV7VK7ZODzHsIwDQYJKoZIhvcNAQEL +BQAwgYoxCzAJBgNVBAYTAlhZMRcwFQYDVQQIDA5DYXN0bGUgQW50aHJheDEYMBYG +A1UEBwwPQXJndW1lbnQgQ2xpbmljMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0aG9udGVzdC5uZXQw +HhcNMTkwNTA4MDEwMjQzWhcNMjcwNzI0MDEwMjQzWjCBijELMAkGA1UEBhMCWFkx +FzAVBgNVBAgMDkNhc3RsZSBBbnRocmF4MRgwFgYDVQQHDA9Bcmd1bWVudCBDbGlu +aWMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQD +DBpzZWxmLXNpZ25lZC5weXRob250ZXN0Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMKdJlyCThkahwoBb7pl5q64Pe9Fn5jrIvzsveHTc97TpjV2 +RLfICnXKrltPk/ohkVl6K5SUZQZwMVzFubkyxE0nZPHYHlpiKWQxbsYVkYv01rix +IFdLvaxxbGYke2jwQao31s4o61AdlsfK1SdpHQUynBBMssqI3SB4XPmcA7e+wEEx +jxjVish4ixA1vuIZOx8yibu+CFCf/geEjoBMF3QPdzULzlrCSw8k/45iZCSoNbvK +DoL4TVV07PHOxpheDh8ZQmepGvU6pVqhb9m4lgmV0OGWHgozd5Ur9CbTVDmxIEz3 +TSoRtNJK7qtyZdGNqwjksQxgZTjM/d/Lm/BJG99AiOmYOjsl9gbQMZgvQmMAtUsI +aMJnQuZ6R+KEpW/TR5qSKLWZSG45z/op+tzI2m+cE6HwTRVAWbcuJxcAA55MZjqU +OOOu3BBYMjS5nf2sQ9uoXsVBFH7i0mQqoW1SLzr9opI8KsWwFxQmO2vBxWYaN+lH +OmwBZBwyODIsmI1YGXmTp09NxRYz3Qe5GCgFzYowpMrcxUC24iduIdMwwhRM7rKg +7GtIWMSrFfuI1XCLRmSlhDbhNN6fVg2f8Bo9PdH9ihiIyxSrc+FOUasUYCCJvlSZ +8hFUlLvcmrZlWuazohm0lsXuMK1JflmQr/DA/uXxP9xzFfRy+RU3jDyxJbRHAgMB +AAGjUzBRMB0GA1UdDgQWBBSQJyxiPMRK01i+0BsV9zUwDiBaHzAfBgNVHSMEGDAW +gBSQJyxiPMRK01i+0BsV9zUwDiBaHzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4ICAQCR+7a7N/m+WLkxPPIA/CB4MOr2Uf8ixTv435Nyv6rXOun0+lTP +ExSZ0uYQ+L0WylItI3cQHULldDueD+s8TGzxf5woaLKf6tqyr0NYhKs+UeNEzDnN +9PHQIhX0SZw3XyXGUgPNBfRCg2ZDdtMMdOU4XlQN/IN/9hbYTrueyY7eXq9hmtI9 +1srftAMqr9SR1JP7aHI6DVgrEsZVMTDnfT8WmLSGLlY1HmGfdEn1Ip5sbo9uSkiH +AEPgPfjYIvR5LqTOMn4KsrlZyBbFIDh9Sl99M1kZzgH6zUGVLCDg1y6Cms69fx/e +W1HoIeVkY4b4TY7Bk7JsqyNhIuqu7ARaxkdaZWhYaA2YyknwANdFfNpfH+elCLIk +BUt5S3f4i7DaUePTvKukCZiCq4Oyln7RcOn5If73wCeLB/ZM9Ei1HforyLWP1CN8 +XLfpHaoeoPSWIveI0XHUl65LsPN2UbMbul/F23hwl+h8+BLmyAS680Yhn4zEN6Ku +B7Po90HoFa1Du3bmx4jsN73UkT/dwMTi6K072FbipnC1904oGlWmLwvAHvrtxxmL +Pl3pvEaZIu8wa/PNF6Y7J7VIewikIJq6Ta6FrWeFfzMWOj2qA1ZZi6fUaDSNYvuV +J5quYKCc/O+I/yDDf8wyBbZ/gvUXzUHTMYGG+bFrn1p7XDbYYeEJ6R/xEg== +-----END CERTIFICATE----- diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -1322,6 +1322,7 @@ test_peepholer test_pyclbr test_pyexpat + test_ssl # overridden by test_ssl_jy test_stringprep # UnicodeDecodeError test_threadsignals test_transformer diff --git a/Lib/test/selfsigned_pythontestdotnet.pem b/Lib/test/selfsigned_pythontestdotnet.pem --- a/Lib/test/selfsigned_pythontestdotnet.pem +++ b/Lib/test/selfsigned_pythontestdotnet.pem @@ -1,16 +1,34 @@ -----BEGIN CERTIFICATE----- -MIIClTCCAf6gAwIBAgIJAKGU95wKR8pTMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV -BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAMMGnNlbGYtc2lnbmVkLnB5dGhv -bnRlc3QubmV0MB4XDTE0MTEwMjE4MDkyOVoXDTI0MTAzMDE4MDkyOVowcDELMAkG -A1UEBhMCWFkxFzAVBgNVBAcMDkNhc3RsZSBBbnRocmF4MSMwIQYDVQQKDBpQeXRo -b24gU29mdHdhcmUgRm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0 -aG9udGVzdC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANDXQXW9tjyZ -Xt0Iv2tLL1+jinr4wGg36ioLDLFkMf+2Y1GL0v0BnKYG4N1OKlAU15LXGeGer8vm -Sv/yIvmdrELvhAbbo3w4a9TMYQA4XkIVLdvu3mvNOAet+8PMJxn26dbDhG809ALv -EHY57lQsBS3G59RZyBPVqAqmImWNJnVzAgMBAAGjNzA1MCUGA1UdEQQeMByCGnNl -bGYtc2lnbmVkLnB5dGhvbnRlc3QubmV0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN -AQEFBQADgYEAIuzAhgMouJpNdf3URCHIineyoSt6WK/9+eyUcjlKOrDoXNZaD72h -TXMeKYoWvJyVcSLKL8ckPtDobgP2OTt0UkyAaj0n+ZHaqq1lH2yVfGUA1ILJv515 -C8BqbvVZuqm3i7ygmw3bqE/lYMgOrYtXXnqOrz6nvsE6Yc9V9rFflOM= +MIIF9zCCA9+gAwIBAgIUH98b4Fw/DyugC9cV7VK7ZODzHsIwDQYJKoZIhvcNAQEL +BQAwgYoxCzAJBgNVBAYTAlhZMRcwFQYDVQQIDA5DYXN0bGUgQW50aHJheDEYMBYG +A1UEBwwPQXJndW1lbnQgQ2xpbmljMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0aG9udGVzdC5uZXQw +HhcNMTkwNTA4MDEwMjQzWhcNMjcwNzI0MDEwMjQzWjCBijELMAkGA1UEBhMCWFkx +FzAVBgNVBAgMDkNhc3RsZSBBbnRocmF4MRgwFgYDVQQHDA9Bcmd1bWVudCBDbGlu +aWMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQD +DBpzZWxmLXNpZ25lZC5weXRob250ZXN0Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMKdJlyCThkahwoBb7pl5q64Pe9Fn5jrIvzsveHTc97TpjV2 +RLfICnXKrltPk/ohkVl6K5SUZQZwMVzFubkyxE0nZPHYHlpiKWQxbsYVkYv01rix +IFdLvaxxbGYke2jwQao31s4o61AdlsfK1SdpHQUynBBMssqI3SB4XPmcA7e+wEEx +jxjVish4ixA1vuIZOx8yibu+CFCf/geEjoBMF3QPdzULzlrCSw8k/45iZCSoNbvK +DoL4TVV07PHOxpheDh8ZQmepGvU6pVqhb9m4lgmV0OGWHgozd5Ur9CbTVDmxIEz3 +TSoRtNJK7qtyZdGNqwjksQxgZTjM/d/Lm/BJG99AiOmYOjsl9gbQMZgvQmMAtUsI +aMJnQuZ6R+KEpW/TR5qSKLWZSG45z/op+tzI2m+cE6HwTRVAWbcuJxcAA55MZjqU +OOOu3BBYMjS5nf2sQ9uoXsVBFH7i0mQqoW1SLzr9opI8KsWwFxQmO2vBxWYaN+lH +OmwBZBwyODIsmI1YGXmTp09NxRYz3Qe5GCgFzYowpMrcxUC24iduIdMwwhRM7rKg +7GtIWMSrFfuI1XCLRmSlhDbhNN6fVg2f8Bo9PdH9ihiIyxSrc+FOUasUYCCJvlSZ +8hFUlLvcmrZlWuazohm0lsXuMK1JflmQr/DA/uXxP9xzFfRy+RU3jDyxJbRHAgMB +AAGjUzBRMB0GA1UdDgQWBBSQJyxiPMRK01i+0BsV9zUwDiBaHzAfBgNVHSMEGDAW +gBSQJyxiPMRK01i+0BsV9zUwDiBaHzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4ICAQCR+7a7N/m+WLkxPPIA/CB4MOr2Uf8ixTv435Nyv6rXOun0+lTP +ExSZ0uYQ+L0WylItI3cQHULldDueD+s8TGzxf5woaLKf6tqyr0NYhKs+UeNEzDnN +9PHQIhX0SZw3XyXGUgPNBfRCg2ZDdtMMdOU4XlQN/IN/9hbYTrueyY7eXq9hmtI9 +1srftAMqr9SR1JP7aHI6DVgrEsZVMTDnfT8WmLSGLlY1HmGfdEn1Ip5sbo9uSkiH +AEPgPfjYIvR5LqTOMn4KsrlZyBbFIDh9Sl99M1kZzgH6zUGVLCDg1y6Cms69fx/e +W1HoIeVkY4b4TY7Bk7JsqyNhIuqu7ARaxkdaZWhYaA2YyknwANdFfNpfH+elCLIk +BUt5S3f4i7DaUePTvKukCZiCq4Oyln7RcOn5If73wCeLB/ZM9Ei1HforyLWP1CN8 +XLfpHaoeoPSWIveI0XHUl65LsPN2UbMbul/F23hwl+h8+BLmyAS680Yhn4zEN6Ku +B7Po90HoFa1Du3bmx4jsN73UkT/dwMTi6K072FbipnC1904oGlWmLwvAHvrtxxmL +Pl3pvEaZIu8wa/PNF6Y7J7VIewikIJq6Ta6FrWeFfzMWOj2qA1ZZi6fUaDSNYvuV +J5quYKCc/O+I/yDDf8wyBbZ/gvUXzUHTMYGG+bFrn1p7XDbYYeEJ6R/xEg== -----END CERTIFICATE----- diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -4,6 +4,7 @@ import sys import unittest from test import test_support as support +from test.script_helper import assert_python_ok import asyncore import socket import select @@ -13,11 +14,12 @@ import os import errno import pprint -import tempfile +import shutil import urllib2 import traceback import weakref import platform +import re import functools from contextlib import closing @@ -25,6 +27,9 @@ PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) HOST = support.HOST +IS_LIBRESSL = ssl.OPENSSL_VERSION.startswith('LibreSSL') +IS_OPENSSL_1_1 = not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0) + def data_file(*name): file = os.path.join(os.path.dirname(__file__), *name) @@ -62,20 +67,30 @@ SIGNED_CERTFILE = data_file("keycert3.pem") SIGNED_CERTFILE2 = data_file("keycert4.pem") SIGNING_CA = data_file("pycacert.pem") +# cert with all kinds of subject alt names +ALLSANFILE = data_file("allsans.pem") REMOTE_HOST = "self-signed.pythontest.net" REMOTE_ROOT_CERT = data_file("selfsigned_pythontestdotnet.pem") EMPTYCERT = data_file("nullcert.pem") BADCERT = data_file("badcert.pem") -WRONGCERT = data_file("XXXnonexisting.pem") +NONEXISTINGCERT = data_file("XXXnonexisting.pem") BADKEY = data_file("badkey.pem") NOKIACERT = data_file("nokia.pem") NULLBYTECERT = data_file("nullbytecert.pem") - -DHFILE = data_file("dh1024.pem") +TALOS_INVALID_CRLDP = data_file("talos-2019-0758.pem") + +DHFILE = data_file("ffdh3072.pem") BYTES_DHFILE = DHFILE.encode(sys.getfilesystemencoding()) +# Not defined in all versions of OpenSSL +OP_NO_COMPRESSION = getattr(ssl, "OP_NO_COMPRESSION", 0) +OP_SINGLE_DH_USE = getattr(ssl, "OP_SINGLE_DH_USE", 0) +OP_SINGLE_ECDH_USE = getattr(ssl, "OP_SINGLE_ECDH_USE", 0) +OP_CIPHER_SERVER_PREFERENCE = getattr(ssl, "OP_CIPHER_SERVER_PREFERENCE", 0) +OP_ENABLE_MIDDLEBOX_COMPAT = getattr(ssl, "OP_ENABLE_MIDDLEBOX_COMPAT", 0) + def handle_error(prefix): exc_format = ' '.join(traceback.format_exception(*sys.exc_info())) @@ -151,6 +166,36 @@ else: return func +def skip_if_openssl_cnf_minprotocol_gt_tls1(func): + """Skip a test if the OpenSSL config MinProtocol is > TLSv1. + OS distros with an /etc/ssl/openssl.cnf and MinProtocol set often do so to + require TLSv1.2 or higher (Debian Buster). Some of our tests for older + protocol versions will fail under such a config. + Alternative workaround: Run this test in a process with + OPENSSL_CONF=/dev/null in the environment. + """ + @functools.wraps(func) + def f(*args, **kwargs): + openssl_cnf = os.environ.get("OPENSSL_CONF", "/etc/ssl/openssl.cnf") + try: + with open(openssl_cnf, "r") as config: + for line in config: + match = re.match(r"MinProtocol\s*=\s*(TLSv\d+\S*)", line) + if match: + tls_ver = match.group(1) + if tls_ver > "TLSv1": + raise unittest.SkipTest( + "%s has MinProtocol = %s which is > TLSv1." % + (openssl_cnf, tls_ver)) + except (EnvironmentError, UnicodeDecodeError) as err: + # no config file found, etc. + if support.verbose: + sys.stdout.write("\n Could not scan %s for MinProtocol: %s\n" + % (openssl_cnf, err)) + return func(*args, **kwargs) + return f + + needs_sni = unittest.skipUnless(ssl.HAS_SNI, "SNI support needed for this test") @@ -168,7 +213,13 @@ ssl.OP_NO_COMPRESSION self.assertIn(ssl.HAS_SNI, {True, False}) self.assertIn(ssl.HAS_ECDH, {True, False}) - + ssl.OP_NO_SSLv2 + ssl.OP_NO_SSLv3 + ssl.OP_NO_TLSv1 + # ssl.OP_NO_TLSv1_3 Not yet supported in Jython ssl.py + if ssl.OPENSSL_VERSION_INFO >= (1, 0, 1): + ssl.OP_NO_TLSv1_1 + ssl.OP_NO_TLSv1_2 def test_random(self): v = ssl.RAND_status() @@ -181,7 +232,6 @@ self.assertRaises(TypeError, ssl.RAND_egd, 'foo', 1) ssl.RAND_add("this is a random string", 75.0) - @unittest.skipIf(support.is_jython, "Jython does not have _ssl, therefore this test needs to be rewritten") def test_parse_cert(self): # note that this uses an 'unofficial' function in _ssl.c, # provided solely for this test, to exercise the certificate @@ -196,9 +246,9 @@ (('commonName', 'localhost'),)) ) # Note the next three asserts will fail if the keys are regenerated - self.assertEqual(p['notAfter'], asn1time('Oct 5 23:01:56 2020 GMT')) - self.assertEqual(p['notBefore'], asn1time('Oct 8 23:01:56 2010 GMT')) - self.assertEqual(p['serialNumber'], 'D7C7381919AFC24E') + self.assertEqual(p['notAfter'], asn1time('Aug 26 14:23:15 2028 GMT')) + self.assertEqual(p['notBefore'], asn1time('Aug 29 14:23:15 2018 GMT')) + self.assertEqual(p['serialNumber'], '98A7CF88C74A32ED') self.assertEqual(p['subject'], ((('countryName', 'XY'),), (('localityName', 'Castle Anthrax'),), @@ -222,7 +272,27 @@ self.assertEqual(p['crlDistributionPoints'], ('http://SVRIntl-G3-crl.verisign.com/SVRIntlG3.crl',)) - @unittest.skipIf(support.is_jython, "Jython does not have _ssl, therefore this test needs to be rewritten") + def test_parse_cert_CVE_2019_5010(self): + p = ssl._ssl._test_decode_cert(TALOS_INVALID_CRLDP) + if support.verbose: + sys.stdout.write("\n" + pprint.pformat(p) + "\n") + self.assertEqual( + p, + { + 'issuer': ( + (('countryName', 'UK'),), (('commonName', 'cody-ca'),)), + 'notAfter': 'Jun 14 18:00:58 2028 GMT', + 'notBefore': 'Jun 18 18:00:58 2018 GMT', + 'serialNumber': '02', + 'subject': ((('countryName', 'UK'),), + (('commonName', + 'codenomicon-vm-2.test.lal.cisco.com'),)), + 'subjectAltName': ( + ('DNS', 'codenomicon-vm-2.test.lal.cisco.com'),), + 'version': 3 + } + ) + def test_parse_cert_CVE_2013_4238(self): p = ssl._ssl._test_decode_cert(NULLBYTECERT) if support.verbose: @@ -252,6 +322,27 @@ self.assertEqual(p['subjectAltName'], san) + def test_parse_all_sans(self): + p = ssl._ssl._test_decode_cert(ALLSANFILE) + self.assertEqual(p['subjectAltName'], + ( + ('DNS', 'allsans'), + ('othername', ''), + ('othername', ''), + ('email', 'user at example.org'), + ('DNS', 'www.example.org'), + ('DirName', + ((('countryName', 'XY'),), + (('localityName', 'Castle Anthrax'),), + (('organizationName', 'Python Software Foundation'),), + (('commonName', 'dirname example'),))), + ('URI', 'https://www.python.org/'), + ('IP Address', '127.0.0.1'), + ('IP Address', '0:0:0:0:0:0:0:1\n'), + ('Registered ID', '1.2.3.4.5') + ) + ) + def test_DER_to_PEM(self): with open(CAFILE_CACERT, 'r') as f: pem = f.read() @@ -288,9 +379,9 @@ self.assertGreaterEqual(status, 0) self.assertLessEqual(status, 15) # Version string as returned by {Open,Libre}SSL, the format might change - if "LibreSSL" in s: - self.assertTrue(s.startswith("LibreSSL {:d}.{:d}".format(major, minor)), - (s, t)) + if IS_LIBRESSL: + self.assertTrue(s.startswith("LibreSSL {:d}".format(major)), + (s, t, hex(n))) else: self.assertTrue(s.startswith("OpenSSL {:d}.{:d}.{:d}".format(major, minor, fix)), (s, t)) @@ -316,6 +407,7 @@ self.assertRaises(socket.error, ss.recvfrom_into, bytearray(b'x'), 1) self.assertRaises(socket.error, ss.send, b'x') self.assertRaises(socket.error, ss.sendto, b'x', ('0.0.0.0', 0)) + self.assertRaises(NotImplementedError, ss.dup) def test_timeout(self): # Issue #8524: when creating an SSL socket, the timeout of the @@ -348,16 +440,41 @@ s.connect, (HOST, 8080)) with self.assertRaises(IOError) as cm: with closing(socket.socket()) as sock: - ssl.wrap_socket(sock, certfile=WRONGCERT) + ssl.wrap_socket(sock, certfile=NONEXISTINGCERT) + self.assertEqual(cm.exception.errno, errno.ENOENT) + with self.assertRaises(IOError) as cm: + with closing(socket.socket()) as sock: + ssl.wrap_socket(sock, + certfile=CERTFILE, keyfile=NONEXISTINGCERT) self.assertEqual(cm.exception.errno, errno.ENOENT) with self.assertRaises(IOError) as cm: with closing(socket.socket()) as sock: - ssl.wrap_socket(sock, certfile=CERTFILE, keyfile=WRONGCERT) + ssl.wrap_socket(sock, + certfile=NONEXISTINGCERT, keyfile=NONEXISTINGCERT) self.assertEqual(cm.exception.errno, errno.ENOENT) - with self.assertRaises(IOError) as cm: - with closing(socket.socket()) as sock: - ssl.wrap_socket(sock, certfile=WRONGCERT, keyfile=WRONGCERT) - self.assertEqual(cm.exception.errno, errno.ENOENT) + + def bad_cert_test(self, certfile): + """Check that trying to use the given client certificate fails""" + certfile = os.path.join(os.path.dirname(__file__) or os.curdir, + certfile) + sock = socket.socket() + self.addCleanup(sock.close) + with self.assertRaises(ssl.SSLError): + ssl.wrap_socket(sock, + certfile=certfile, + ssl_version=ssl.PROTOCOL_TLSv1) + + def test_empty_cert(self): + """Wrapping with an empty cert file""" + self.bad_cert_test("nullcert.pem") + + def test_malformed_cert(self): + """Wrapping with a badly formatted certificate (syntax error)""" + self.bad_cert_test("badcert.pem") + + def test_malformed_key(self): + """Wrapping with a badly formatted key (syntax error)""" + self.bad_cert_test("badkey.pem") def test_match_hostname(self): def ok(cert, hostname): @@ -583,32 +700,31 @@ self.assertIsInstance(val, ssl._ASN1Object) self.assertRaises(ValueError, ssl._ASN1Object, 'serverAuth') - # TODO Jython better asn1 support, though not sure there's much use for it - # val = ssl._ASN1Object.fromnid(129) - # self.assertEqual(val, expected) - # self.assertIsInstance(val, ssl._ASN1Object) - # self.assertRaises(ValueError, ssl._ASN1Object.fromnid, -1) - # with self.assertRaisesRegexp(ValueError, "unknown NID 100000"): - # ssl._ASN1Object.fromnid(100000) - # for i in range(1000): - # try: - # obj = ssl._ASN1Object.fromnid(i) - # except ValueError: - # pass - # else: - # self.assertIsInstance(obj.nid, int) - # self.assertIsInstance(obj.shortname, str) - # self.assertIsInstance(obj.longname, str) - # self.assertIsInstance(obj.oid, (str, type(None))) - # - # val = ssl._ASN1Object.fromname('TLS Web Server Authentication') - # self.assertEqual(val, expected) - # self.assertIsInstance(val, ssl._ASN1Object) - # self.assertEqual(ssl._ASN1Object.fromname('serverAuth'), expected) - # self.assertEqual(ssl._ASN1Object.fromname('1.3.6.1.5.5.7.3.1'), - # expected) - # with self.assertRaisesRegexp(ValueError, "unknown object 'serverauth'"): - # ssl._ASN1Object.fromname('serverauth') + val = ssl._ASN1Object.fromnid(129) + self.assertEqual(val, expected) + self.assertIsInstance(val, ssl._ASN1Object) + self.assertRaises(ValueError, ssl._ASN1Object.fromnid, -1) + with self.assertRaisesRegexp(ValueError, "unknown NID 100000"): + ssl._ASN1Object.fromnid(100000) + for i in range(1000): + try: + obj = ssl._ASN1Object.fromnid(i) + except ValueError: + pass + else: + self.assertIsInstance(obj.nid, int) + self.assertIsInstance(obj.shortname, str) + self.assertIsInstance(obj.longname, str) + self.assertIsInstance(obj.oid, (str, type(None))) + + val = ssl._ASN1Object.fromname('TLS Web Server Authentication') + self.assertEqual(val, expected) + self.assertIsInstance(val, ssl._ASN1Object) + self.assertEqual(ssl._ASN1Object.fromname('serverAuth'), expected) + self.assertEqual(ssl._ASN1Object.fromname('1.3.6.1.5.5.7.3.1'), + expected) + with self.assertRaisesRegexp(ValueError, "unknown object 'serverauth'"): + ssl._ASN1Object.fromname('serverauth') def test_purpose_enum(self): val = ssl._ASN1Object('1.3.6.1.5.5.7.3.1') @@ -720,7 +836,6 @@ ctx = ssl.SSLContext(proto) self.assertEqual(ctx.protocol, proto) - @unittest.skipIf(support.is_jython, "Currently not supported") def test_ciphers(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) ctx.set_ciphers("ALL") @@ -732,17 +847,20 @@ def test_options(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) # OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value - self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3, - ctx.options) + default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3) + # SSLContext also enables these by default + default |= (OP_NO_COMPRESSION | OP_CIPHER_SERVER_PREFERENCE | + OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE | + OP_ENABLE_MIDDLEBOX_COMPAT) + self.assertEqual(default, ctx.options) ctx.options |= ssl.OP_NO_TLSv1 - self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1, - ctx.options) + self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options) if can_clear_options(): - ctx.options = (ctx.options & ~ssl.OP_NO_SSLv2) | ssl.OP_NO_TLSv1 - self.assertEqual(ssl.OP_ALL | ssl.OP_NO_TLSv1 | ssl.OP_NO_SSLv3, - ctx.options) + ctx.options = (ctx.options & ~ssl.OP_NO_TLSv1) + self.assertEqual(default, ctx.options) ctx.options = 0 - self.assertEqual(0, ctx.options) + # Ubuntu has OP_NO_SSLv3 forced on by default + self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3) else: with self.assertRaises(ValueError): ctx.options = 0 @@ -797,7 +915,7 @@ ctx.load_cert_chain(CERTFILE, keyfile=CERTFILE) self.assertRaises(TypeError, ctx.load_cert_chain, keyfile=CERTFILE) with self.assertRaises(IOError) as cm: - ctx.load_cert_chain(WRONGCERT) + ctx.load_cert_chain(NONEXISTINGCERT) self.assertEqual(cm.exception.errno, errno.ENOENT) with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"): ctx.load_cert_chain(BADCERT) @@ -883,7 +1001,7 @@ self.assertRaises(TypeError, ctx.load_verify_locations) self.assertRaises(TypeError, ctx.load_verify_locations, None, None, None) with self.assertRaises(IOError) as cm: - ctx.load_verify_locations(WRONGCERT) + ctx.load_verify_locations(NONEXISTINGCERT) self.assertEqual(cm.exception.errno, errno.ENOENT) with self.assertRaises(IOError): ctx.load_verify_locations(u'') @@ -953,8 +1071,14 @@ ctx.load_verify_locations(cadata=b"broken") - @unittest.skipIf(support.is_jython, "Not yet supported on Jython") def test_load_dh_params(self): + filename = u'dhp?r?m.pem' + fs_encoding = sys.getfilesystemencoding() + try: + filename.encode(fs_encoding) + except UnicodeEncodeError: + self.skipTest("filename %r cannot be encoded to the filesystem encoding %r" % (filename, fs_encoding)) + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) ctx.load_dh_params(DHFILE) if os.name != 'nt': @@ -962,10 +1086,14 @@ self.assertRaises(TypeError, ctx.load_dh_params) self.assertRaises(TypeError, ctx.load_dh_params, None) with self.assertRaises(IOError) as cm: - ctx.load_dh_params(WRONGCERT) + ctx.load_dh_params(NONEXISTINGCERT) self.assertEqual(cm.exception.errno, errno.ENOENT) with self.assertRaises(ssl.SSLError) as cm: ctx.load_dh_params(CERTFILE) + with support.temp_dir() as d: + fname = os.path.join(d, filename) + shutil.copy(DHFILE, fname) + ctx.load_dh_params(fname) @skip_if_broken_ubuntu_ssl def test_session_stats(self): @@ -1033,16 +1161,13 @@ ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) self.assertEqual(ctx.cert_store_stats(), {'x509_ca': 0, 'crl': 0, 'x509': 0}) - - # Jython x509 will grow by 1 while openssl remains 0 - # TODO investgate deeper ctx.load_cert_chain(CERTFILE) self.assertEqual(ctx.cert_store_stats(), - {'x509_ca': 0, 'crl': 0, 'x509': 1}) + {'x509_ca': 0, 'crl': 0, 'x509': 0}) ctx.load_verify_locations(CERTFILE) self.assertEqual(ctx.cert_store_stats(), - {'x509_ca': 0, 'crl': 0, 'x509': 2}) - ctx.load_verify_locations(REMOTE_ROOT_CERT) + {'x509_ca': 0, 'crl': 0, 'x509': 1}) + ctx.load_verify_locations(CAFILE_CACERT) self.assertEqual(ctx.cert_store_stats(), {'x509_ca': 1, 'crl': 0, 'x509': 2}) @@ -1072,11 +1197,12 @@ # # see this sample code on how we might be able to decode: # https://svn.apache.org/repos/asf/cxf/tags/cxf-2.4.4/distribution/src/main/release/samples/sts_issue_operation/src/main/java/demo/sts/provider/cert/CRLVerifier.java + # Subject and issuer ordering also differs vs CPython 2.7 latest with open(CAFILE_CACERT) as f: pem = f.read() - der = ssl.PEM_cert_to_DER_cert(pem) - self.assertEqual(ctx.get_ca_certs(True), [der]) + der = ssl.PEM_cert_to_DER_cert(pem) + self.assertEqual(ctx.get_ca_certs(True), [der]) def test_load_default_certs(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) @@ -1094,13 +1220,14 @@ self.assertRaises(TypeError, ctx.load_default_certs, 'SERVER_AUTH') @unittest.skipIf(sys.platform == "win32", "not-Windows specific") + @unittest.skipIf(IS_LIBRESSL, "LibreSSL doesn't support env vars") def test_load_default_certs_env(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) with support.EnvironmentVarGuard() as env: env["SSL_CERT_DIR"] = CAPATH env["SSL_CERT_FILE"] = CERTFILE ctx.load_default_certs() - self.assertEqual(ctx.cert_store_stats(), {"crl": 0, "x509": 4, "x509_ca": 0}) + self.assertEqual(ctx.cert_store_stats(), {"crl": 0, "x509": 1, "x509_ca": 0}) @unittest.skipUnless(sys.platform == "win32", "Windows specific") def test_load_default_certs_env_windows(self): @@ -1116,16 +1243,29 @@ stats["x509"] += 1 self.assertEqual(ctx.cert_store_stats(), stats) + def _assert_context_options(self, ctx): + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + if OP_NO_COMPRESSION != 0: + self.assertEqual(ctx.options & OP_NO_COMPRESSION, + OP_NO_COMPRESSION) + if OP_SINGLE_DH_USE != 0: + self.assertEqual(ctx.options & OP_SINGLE_DH_USE, + OP_SINGLE_DH_USE) + if OP_SINGLE_ECDH_USE != 0: + self.assertEqual(ctx.options & OP_SINGLE_ECDH_USE, + OP_SINGLE_ECDH_USE) + if OP_CIPHER_SERVER_PREFERENCE != 0: + self.assertEqual(ctx.options & OP_CIPHER_SERVER_PREFERENCE, + OP_CIPHER_SERVER_PREFERENCE) + def test_create_default_context(self): ctx = ssl.create_default_context() + self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) self.assertTrue(ctx.check_hostname) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) - # self.assertEqual( - # ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0), - # getattr(ssl, "OP_NO_COMPRESSION", 0), - # ) + self._assert_context_options(ctx) + with open(SIGNING_CA) as f: cadata = f.read().decode("ascii") @@ -1133,40 +1273,24 @@ cadata=cadata) self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) - # self.assertEqual( - # ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0), - # getattr(ssl, "OP_NO_COMPRESSION", 0), - # ) + self._assert_context_options(ctx) ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) - # self.assertEqual( - # ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0), - # getattr(ssl, "OP_NO_COMPRESSION", 0), - # ) - # self.assertEqual( - # ctx.options & getattr(ssl, "OP_SINGLE_DH_USE", 0), - # getattr(ssl, "OP_SINGLE_DH_USE", 0), - # ) - # self.assertEqual( - # ctx.options & getattr(ssl, "OP_SINGLE_ECDH_USE", 0), - # getattr(ssl, "OP_SINGLE_ECDH_USE", 0), - # ) + self._assert_context_options(ctx) def test__create_stdlib_context(self): ctx = ssl._create_stdlib_context() self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) self.assertFalse(ctx.check_hostname) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self._assert_context_options(ctx) ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1) self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self._assert_context_options(ctx) ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1, cert_reqs=ssl.CERT_REQUIRED, @@ -1174,12 +1298,63 @@ self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) self.assertTrue(ctx.check_hostname) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self._assert_context_options(ctx) ctx = ssl._create_stdlib_context(purpose=ssl.Purpose.CLIENT_AUTH) self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) - self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self._assert_context_options(ctx) + + def test__https_verify_certificates(self): + # Unit test to check the contect factory mapping + # The factories themselves are tested above + # This test will fail by design if run under PYTHONHTTPSVERIFY=0 + # (as will various test_httplib tests) + + # Uses a fresh SSL module to avoid affecting the real one + local_ssl = support.import_fresh_module("ssl") + # Certificate verification is enabled by default + self.assertIs(local_ssl._create_default_https_context, + local_ssl.create_default_context) + # Turn default verification off + local_ssl._https_verify_certificates(enable=False) + self.assertIs(local_ssl._create_default_https_context, + local_ssl._create_unverified_context) + # And back on + local_ssl._https_verify_certificates(enable=True) + self.assertIs(local_ssl._create_default_https_context, + local_ssl.create_default_context) + # The default behaviour is to enable + local_ssl._https_verify_certificates(enable=False) + local_ssl._https_verify_certificates() + self.assertIs(local_ssl._create_default_https_context, + local_ssl.create_default_context) + + def test__https_verify_envvar(self): + # Unit test to check the PYTHONHTTPSVERIFY handling + # Need to use a subprocess so it can still be run under -E + https_is_verified = """import ssl, sys; \ + status = "Error: _create_default_https_context does not verify certs" \ + if ssl._create_default_https_context is \ + ssl._create_unverified_context \ + else None; \ + sys.exit(status)""" + https_is_not_verified = """import ssl, sys; \ + status = "Error: _create_default_https_context verifies certs" \ + if ssl._create_default_https_context is \ + ssl.create_default_context \ + else None; \ + sys.exit(status)""" + extra_env = {} + # Omitting it leaves verification on + assert_python_ok("-c", https_is_verified, **extra_env) + # Setting it to zero turns verification off + extra_env[ssl._https_verify_envvar] = "0" + assert_python_ok("-c", https_is_not_verified, **extra_env) + # Any other value should also leave it on + for setting in ("", "1", "enabled", "foo"): + extra_env[ssl._https_verify_envvar] = setting + assert_python_ok("-c", https_is_verified, **extra_env) def test_check_hostname(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) @@ -1209,14 +1384,13 @@ def test_str(self): # The str() of a SSLError doesn't include the errno e = ssl.SSLError(1, "foo") - self.assertIn("foo", str(e)) + self.assertEqual(str(e), "foo") self.assertEqual(e.errno, 1) # Same for a subclass e = ssl.SSLZeroReturnError(1, "foo") - self.assertIn("foo", str(e)) + self.assertEqual(str(e), "foo") self.assertEqual(e.errno, 1) - @unittest.skipIf(support.is_jython, "TODO") def test_lib_reason(self): # Test the library and reason attributes ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) @@ -1227,7 +1401,6 @@ s = str(cm.exception) self.assertTrue(s.startswith("[PEM: NO_START_LINE] no start line"), s) - @unittest.skipIf(support.is_jython, "TODO") def test_subclass(self): # Check that the appropriate SSLError subclass is raised # (this only tests one of them) @@ -1283,7 +1456,7 @@ cert_reqs=ssl.CERT_REQUIRED, ca_certs=REMOTE_ROOT_CERT) try: - self.assertEqual(errno.EISCONN, s.connect_ex((REMOTE_HOST, 443))) + self.assertEqual(0, s.connect_ex((REMOTE_HOST, 443))) self.assertTrue(s.getpeercert()) finally: s.close() @@ -1300,8 +1473,7 @@ s.setblocking(False) rc = s.connect_ex((REMOTE_HOST, 443)) # EWOULDBLOCK under Windows, EINPROGRESS elsewhere - # Jython added EALREADY, as in Jython connect may have already happened - self.assertIn(rc, (0, errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK)) + self.assertIn(rc, (0, errno.EINPROGRESS, errno.EWOULDBLOCK)) # Wait for connect to finish select.select([], [s], [], 5.0) # Non-blocking handshake @@ -1314,7 +1486,7 @@ except ssl.SSLWantWriteError: select.select([], [s], [], 5.0) # SSL established - #self.assertTrue(s.getpeercert()) + self.assertTrue(s.getpeercert()) finally: s.close() @@ -1329,9 +1501,9 @@ try: s.settimeout(0.0000001) rc = s.connect_ex((REMOTE_HOST, 443)) - if rc == errno.EISCONN: + if rc == 0: self.skipTest("REMOTE_HOST responded too quickly") - self.assertIn(rc, (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK)) + self.assertIn(rc, (errno.EAGAIN, errno.EWOULDBLOCK)) finally: s.close() @@ -1434,7 +1606,6 @@ cert = s.getpeercert() self.assertTrue(cert) - @unittest.skipIf(support.is_jython, "Can't use a socket as a file under Jython") @unittest.skipIf(os.name == "nt", "Can't use a socket as a file under Windows") def test_makefile_close(self): # Issue #5238: creating a file-like object with makefile() shouldn't @@ -1493,7 +1664,6 @@ sys.stdout.write("%s\n" % x) else: self.fail("Got server certificate %s for %s:%s!" % (pem, host, port)) - pem = ssl.get_server_certificate((host, port), ca_certs=cert) if not pem: @@ -1505,7 +1675,6 @@ if support.IPV6_ENABLED: _test_get_server_certificate('ipv6.google.com', 443) - @unittest.skipIf(support.is_jython, "Currently not supported") def test_ciphers(self): remote = (REMOTE_HOST, 443) with support.transient_internet(remote[0]): @@ -1522,35 +1691,6 @@ cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx") s.connect(remote) - def test_algorithms(self): - # Issue #8484: all algorithms should be available when verifying a - # certificate. - # SHA256 was added in OpenSSL 0.9.8 - if ssl.OPENSSL_VERSION_INFO < (0, 9, 8, 0, 15): - self.skipTest("SHA256 not available on %r" % ssl.OPENSSL_VERSION) - # sha256.tbs-internet.com needs SNI to use the correct certificate - if not ssl.HAS_SNI or support.is_jython: # sha256.tbs-internet.com is no longer alive - self.skipTest("SNI needed for this test") - # https://sha2.hboeck.de/ was used until 2011-01-08 (no route to host) - remote = ("sha256.tbs-internet.com", 443) - sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem") - with support.transient_internet("sha256.tbs-internet.com"): - ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - ctx.verify_mode = ssl.CERT_REQUIRED - ctx.load_verify_locations(sha256_cert) - s = ctx.wrap_socket(socket.socket(socket.AF_INET), - server_hostname="sha256.tbs-internet.com") - try: - s.connect(remote) - if support.verbose: - sys.stdout.write("\nCipher with %r is %r\n" % - (remote, s.cipher())) - sys.stdout.write("Certificate is:\n%s\n" % - pprint.pformat(s.getpeercert())) - finally: - s.close() - - @unittest.skipIf(support.is_jython, "On jython preloaded TODO") def test_get_ca_certs_capath(self): # capath certs are loaded on request with support.transient_internet(REMOTE_HOST): @@ -1565,7 +1705,7 @@ self.assertTrue(cert) finally: s.close() - self.assertEqual(len(ctx.get_ca_certs()), 3) + self.assertEqual(len(ctx.get_ca_certs()), 1) @needs_sni def test_context_setget(self): @@ -1586,9 +1726,7 @@ import threading except ImportError: _have_threads = False - -_have_threads = False -if _have_threads: # Jython skip threading tests for now, really don't work :( +else: _have_threads = True from test.ssl_servers import make_https_server @@ -1617,23 +1755,43 @@ self.sock, server_side=True) self.server.selected_npn_protocols.append(self.sslconn.selected_npn_protocol()) self.server.selected_alpn_protocols.append(self.sslconn.selected_alpn_protocol()) - except socket.error as e: - # We treat ConnectionResetError as though it were an - # SSLError - OpenSSL on Ubuntu abruptly closes the - # connection when asked to use an unsupported protocol. - # - # XXX Various errors can have happened here, for example - # a mismatching protocol version, an invalid certificate, - # or a low-level bug. This should be made more discriminating. - if not isinstance(e, ssl.SSLError) and e.errno != errno.ECONNRESET: - raise - self.server.conn_errors.append(e) - if self.server.chatty: - handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n") - self.running = False - self.server.stop() - self.close() - return False + except (ssl.SSLError, socket.error, OSError) as e: + if e.errno in (errno.ECONNRESET, errno.EPIPE, errno.ESHUTDOWN): + # Mimick Python 3: + # + # except (ConnectionResetError, BrokenPipeError): + # + # We treat ConnectionResetError as though it were an + # SSLError - OpenSSL on Ubuntu abruptly closes the + # connection when asked to use an unsupported protocol. + # + # BrokenPipeError is raised in TLS 1.3 mode, when OpenSSL + # tries to send session tickets after handshake. + # https://github.com/openssl/openssl/issues/6342 + self.server.conn_errors.append(str(e)) + if self.server.chatty: + handle_error( + "\n server: bad connection attempt from " + + repr(self.addr) + ":\n") + self.running = False + self.close() + return False + else: + # OSError may occur with wrong protocols, e.g. both + # sides use PROTOCOL_TLS_SERVER. + # + # XXX Various errors can have happened here, for example + # a mismatching protocol version, an invalid certificate, + # or a low-level bug. This should be made more discriminating. + if not isinstance(e, ssl.SSLError) and e.errno != errno.ECONNRESET: + raise + self.server.conn_errors.append(e) + if self.server.chatty: + handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n") + self.running = False + self.server.stop() + self.close() + return False else: if self.server.context.verify_mode == ssl.CERT_REQUIRED: cert = self.sslconn.getpeercert() @@ -1732,7 +1890,7 @@ else: self.context = ssl.SSLContext(ssl_version if ssl_version is not None - else ssl.PROTOCOL_TLSv1) + else ssl.PROTOCOL_TLS) self.context.verify_mode = (certreqs if certreqs is not None else ssl.CERT_NONE) if cacerts: @@ -1892,6 +2050,8 @@ self.join() if support.verbose: sys.stdout.write(" cleanup: successfully joined.\n") + # make sure that ConnectionHandler is removed from socket_map + asyncore.close_all(ignore_all=True) def start(self, flag=None): self.flag = flag @@ -1911,36 +2071,6 @@ self.active = False self.server.close() - def bad_cert_test(certfile): - """ - Launch a server with CERT_REQUIRED, and check that trying to - connect to it with the given client certificate fails. - """ - server = ThreadedEchoServer(CERTFILE, - certreqs=ssl.CERT_REQUIRED, - cacerts=CERTFILE, chatty=False, - connectionchatty=False) - with server: - try: - with closing(socket.socket()) as sock: - s = ssl.wrap_socket(sock, - certfile=certfile, - ssl_version=ssl.PROTOCOL_TLSv1) - s.connect((HOST, server.port)) - except ssl.SSLError as x: - if support.verbose: - sys.stdout.write("\nSSLError is %s\n" % x.args[1]) - except OSError as x: - if support.verbose: - sys.stdout.write("\nOSError is %s\n" % x.args[1]) - except OSError as x: - if x.errno != errno.ENOENT: - raise - if support.verbose: - sys.stdout.write("\OSError is %s\n" % str(x)) - else: - raise AssertionError("Use of invalid cert should have failed!") - def server_params_test(client_context, server_context, indata=b"FOO\n", chatty=True, connectionchatty=False, sni_name=None): """ @@ -2179,22 +2309,38 @@ "check_hostname requires server_hostname"): context.wrap_socket(s) - def test_empty_cert(self): - """Connecting with an empty cert file""" - bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir, - "nullcert.pem")) - def test_malformed_cert(self): - """Connecting with a badly formatted certificate (syntax error)""" - bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir, - "badcert.pem")) - def test_nonexisting_cert(self): - """Connecting with a non-existing cert file""" - bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir, - "wrongcert.pem")) - def test_malformed_key(self): - """Connecting with a badly formatted key (syntax error)""" - bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir, - "badkey.pem")) + def test_wrong_cert(self): + """Connecting when the server rejects the client's certificate + + Launch a server with CERT_REQUIRED, and check that trying to + connect to it with a wrong client certificate fails. + """ + certfile = os.path.join(os.path.dirname(__file__) or os.curdir, + "keycert.pem") + server = ThreadedEchoServer(SIGNED_CERTFILE, + certreqs=ssl.CERT_REQUIRED, + cacerts=SIGNING_CA, chatty=False, + connectionchatty=False) + with server, \ + closing(socket.socket()) as sock, \ + closing(ssl.wrap_socket(sock, + certfile=certfile, + ssl_version=ssl.PROTOCOL_TLSv1)) as s: + try: + # Expect either an SSL error about the server rejecting + # the connection, or a low-level connection reset (which + # sometimes happens on Windows) + s.connect((HOST, server.port)) + except ssl.SSLError as e: + if support.verbose: + sys.stdout.write("\nSSLError is %r\n" % e) + except socket.error as e: + if e.errno != errno.ECONNRESET: + raise + if support.verbose: + sys.stdout.write("\nsocket.error is %r\n" % e) + else: + self.fail("Use of invalid cert should have failed!") def test_rude_shutdown(self): """A brutal shutdown of an SSL server should raise an OSError @@ -2261,6 +2407,7 @@ client_options=ssl.OP_NO_TLSv1) @skip_if_broken_ubuntu_ssl + @skip_if_openssl_cnf_minprotocol_gt_tls1 def test_protocol_sslv23(self): """Connecting to an SSLv23 server with various client options""" if support.verbose: @@ -2338,6 +2485,7 @@ @skip_if_broken_ubuntu_ssl @unittest.skipUnless(hasattr(ssl, "PROTOCOL_TLSv1_1"), "TLS version 1.1 not supported.") + @skip_if_openssl_cnf_minprotocol_gt_tls1 def test_protocol_tlsv1_1(self): """Connecting to a TLSv1.1 server with various client options. Testing against older TLS versions.""" @@ -2466,8 +2614,6 @@ def test_asyncore_server(self): """Check the example asyncore integration.""" - indata = "TEST MESSAGE of mixed case\n" - if support.verbose: sys.stdout.write("\n") @@ -2599,9 +2745,41 @@ # consume data s.read() + # read(-1, buffer) is supported, even though read(-1) is not + data = b"data" + s.send(data) + buffer = bytearray(len(data)) + self.assertEqual(s.read(-1, buffer), len(data)) + self.assertEqual(buffer, data) + + self.assertRaises(NotImplementedError, s.dup) s.write(b"over\n") + + self.assertRaises(ValueError, s.recv, -1) + self.assertRaises(ValueError, s.read, -1) + s.close() + def test_recv_zero(self): + server = ThreadedEchoServer(CERTFILE) + server.__enter__() + self.addCleanup(server.__exit__, None, None) + s = socket.create_connection((HOST, server.port)) + self.addCleanup(s.close) + s = ssl.wrap_socket(s, suppress_ragged_eofs=False) + self.addCleanup(s.close) + + # recv/read(0) should return no data + s.send(b"data") + self.assertEqual(s.recv(0), b"") + self.assertEqual(s.read(0), b"") + self.assertEqual(s.read(), b"data") + + # Should not block if the other end sends no data + s.setblocking(False) + self.assertEqual(s.recv(0), b"") + self.assertEqual(s.recv_into(bytearray()), 0) + def test_handshake_timeout(self): # Issue #5103: SSL handshake must respect the socket timeout server = socket.socket(socket.AF_INET) @@ -2671,7 +2849,7 @@ # Block on the accept and wait on the connection to close. evt.set() remote[0], peer[0] = server.accept() - remote[0].recv(1) + remote[0].send(remote[0].recv(4)) t = threading.Thread(target=serve) t.start() @@ -2679,6 +2857,8 @@ evt.wait() client = context.wrap_socket(socket.socket()) client.connect((host, port)) + client.send(b'data') + client.recv() client_addr = client.getsockname() client.close() t.join() @@ -2702,19 +2882,24 @@ sock.do_handshake() self.assertEqual(cm.exception.errno, errno.ENOTCONN) - def test_default_ciphers(self): - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - try: - # Force a set of weak ciphers on our client context - context.set_ciphers("DES") - except ssl.SSLError: - self.skipTest("no DES cipher available") - with ThreadedEchoServer(CERTFILE, - ssl_version=ssl.PROTOCOL_SSLv23, - chatty=False) as server: - with closing(context.wrap_socket(socket.socket())) as s: - with self.assertRaises(ssl.SSLError): - s.connect((HOST, server.port)) + def test_no_shared_ciphers(self): + server_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + server_context.load_cert_chain(SIGNED_CERTFILE) + client_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + client_context.verify_mode = ssl.CERT_REQUIRED + client_context.check_hostname = True + + # OpenSSL enables all TLS 1.3 ciphers, enforce TLS 1.2 for test + client_context.options |= ssl.OP_NO_TLSv1_3 + # Force different suites on client and master + client_context.set_ciphers("AES128") + server_context.set_ciphers("AES256") + with ThreadedEchoServer(context=server_context) as server: + s = client_context.wrap_socket( + socket.socket(), + server_hostname="localhost") + with self.assertRaises(ssl.SSLError): + s.connect((HOST, server.port)) self.assertIn("no shared cipher", str(server.conn_errors[0])) def test_version_basic(self): @@ -2729,15 +2914,37 @@ with closing(context.wrap_socket(socket.socket())) as s: self.assertIs(s.version(), None) s.connect((HOST, server.port)) - self.assertEqual(s.version(), "TLSv1") + self.assertEqual(s.version(), 'TLSv1') self.assertIs(s.version(), None) + @unittest.skipUnless(ssl.HAS_TLSv1_3, + "test requires TLSv1.3 enabled OpenSSL") + def test_tls1_3(self): + context = ssl.SSLContext(ssl.PROTOCOL_TLS) + context.load_cert_chain(CERTFILE) + # disable all but TLS 1.3 + context.options |= ( + ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2 + ) + with ThreadedEchoServer(context=context) as server: + s = context.wrap_socket(socket.socket()) + with closing(s): + s.connect((HOST, server.port)) + self.assertIn(s.cipher()[0], [ + 'TLS_AES_256_GCM_SHA384', + 'TLS_CHACHA20_POLY1305_SHA256', + 'TLS_AES_128_GCM_SHA256', + ]) + @unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL") def test_default_ecdh_curve(self): # Issue #21015: elliptic curve-based Diffie Hellman key exchange # should be enabled by default on SSL contexts. context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) context.load_cert_chain(CERTFILE) + # TLSv1.3 defaults to PFS key agreement and no longer has KEA in + # cipher name. + context.options |= ssl.OP_NO_TLSv1_3 # Prior to OpenSSL 1.0.0, ECDH ciphers have to be enabled # explicitly using the 'ECCdraft' cipher alias. Otherwise, # our default cipher list should prefer ECDH-based ciphers @@ -2871,24 +3078,37 @@ (['http/3.0', 'http/4.0'], None) ] for client_protocols, expected in protocol_tests: - server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) server_context.load_cert_chain(CERTFILE) server_context.set_alpn_protocols(server_protocols) - client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) client_context.load_cert_chain(CERTFILE) client_context.set_alpn_protocols(client_protocols) - stats = server_params_test(client_context, server_context, - chatty=True, connectionchatty=True) - - msg = "failed trying %s (s) and %s (c).\n" \ - "was expecting %s, but got %%s from the %%s" \ - % (str(server_protocols), str(client_protocols), - str(expected)) - client_result = stats['client_alpn_protocol'] - self.assertEqual(client_result, expected, msg % (client_result, "client")) - server_result = stats['server_alpn_protocols'][-1] \ - if len(stats['server_alpn_protocols']) else 'nothing' - self.assertEqual(server_result, expected, msg % (server_result, "server")) + + try: + stats = server_params_test(client_context, + server_context, + chatty=True, + connectionchatty=True) + except ssl.SSLError as e: + stats = e + + if (expected is None and IS_OPENSSL_1_1 + and ssl.OPENSSL_VERSION_INFO < (1, 1, 0, 6)): + # OpenSSL 1.1.0 to 1.1.0e raises handshake error + self.assertIsInstance(stats, ssl.SSLError) + else: + msg = "failed trying %s (s) and %s (c).\n" \ + "was expecting %s, but got %%s from the %%s" \ + % (str(server_protocols), str(client_protocols), + str(expected)) + client_result = stats['client_alpn_protocol'] + self.assertEqual(client_result, expected, + msg % (client_result, "client")) + server_result = stats['server_alpn_protocols'][-1] \ + if len(stats['server_alpn_protocols']) else 'nothing' + self.assertEqual(server_result, expected, + msg % (server_result, "server")) def test_selected_npn_protocol(self): # selected_npn_protocol() is None unless NPN is used @@ -3046,8 +3266,9 @@ self.assertRaises(ValueError, s.write, b'hello') -def test_main(verbose=False): - if support.verbose: + +def test_main(verbose=False,tests=[]): + if verbose or support.verbose: plats = { 'Linux': platform.linux_distribution, 'Mac': platform.mac_ver, @@ -3078,20 +3299,22 @@ if not os.path.exists(filename): raise support.TestFailed("Can't read certificate file %r" % filename) - tests = [ContextTests, BasicTests, BasicSocketTests, SSLErrorTests] - - if support.is_resource_enabled('network'): - tests.append(NetworkedTests) - - if _have_threads: - thread_info = support.threading_setup() - if thread_info: - tests.append(ThreadedTests) + used_threads = False + if not tests: + tests = [ContextTests, BasicTests, BasicSocketTests, SSLErrorTests] + if support.is_resource_enabled('network'): + tests.append(NetworkedTests) + + if _have_threads: + used_threads = True + thread_info = support.threading_setup() + if thread_info: + tests.append(ThreadedTests) try: support.run_unittest(*tests) finally: - if _have_threads: + if used_threads: support.threading_cleanup(*thread_info) if __name__ == "__main__": diff --git a/Lib/test/test_ssl_jy.py b/Lib/test/test_ssl_jy.py new file mode 100644 --- /dev/null +++ b/Lib/test/test_ssl_jy.py @@ -0,0 +1,231 @@ +# Jython variations on the test_ssl tests are concentrated here, where possible +# Due to the structure of the module, some functions still have to be modified +# directly in the original, but this reduces the diff and ongoing merge effort + +import errno +import select +import socket +import ssl +import sys +import unittest +import test.test_ssl +from test.test_ssl import BasicTests +from test.test_ssl import can_clear_options, support, skip_if_broken_ubuntu_ssl +from test.test_ssl import CAPATH, CERTFILE, CAFILE_CACERT +from test.test_ssl import REMOTE_HOST, REMOTE_ROOT_CERT + + + +class BasicSocketTests(test.test_ssl.BasicSocketTests): + @unittest.skip("Jython does not have _ssl, therefore this test needs to be rewritten") + def test_parse_cert(self): + None + + @unittest.skip("Jython does not have _ssl, therefore this test needs to be rewritten") + def test_parse_cert_CVE_2013_4238(self): + None + + @unittest.skip("Jython does not have _ssl, therefore this test needs to be rewritten") + def test_parse_cert_CVE_2019_5010(self): + None + + @unittest.skip("Jython does not have _ssl, therefore this test needs to be rewritten") + def test_parse_all_sans(self): + None + + def test_asn1object(self): + # Abbreviated version of parent test up to unsupported part + # TODO Jython better asn1 support, though not sure there's much use for + # it + expected = (129, 'serverAuth', 'TLS Web Server Authentication', + '1.3.6.1.5.5.7.3.1') + + val = ssl._ASN1Object('1.3.6.1.5.5.7.3.1') + self.assertEqual(val, expected) + self.assertEqual(val.nid, 129) + self.assertEqual(val.shortname, 'serverAuth') + self.assertEqual(val.longname, 'TLS Web Server Authentication') + self.assertEqual(val.oid, '1.3.6.1.5.5.7.3.1') + self.assertIsInstance(val, ssl._ASN1Object) + self.assertRaises(ValueError, ssl._ASN1Object, 'serverAuth') + + +class ContextTests(test.test_ssl.ContextTests): + @unittest.skip("Currently not supported") + def test_ciphers(self): + None + + @skip_if_broken_ubuntu_ssl + def test_options(self): + # new default options in later 2.7 versions not yet supported + # See CPython b8eaec697a2b5d9d2def2950a0aa50e8ffcf1059 + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + # OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value + self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3, + ctx.options) + ctx.options |= ssl.OP_NO_TLSv1 + self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1, + ctx.options) + if can_clear_options(): + ctx.options = (ctx.options & ~ssl.OP_NO_SSLv2) | ssl.OP_NO_TLSv1 + self.assertEqual(ssl.OP_ALL | ssl.OP_NO_TLSv1 | ssl.OP_NO_SSLv3, + ctx.options) + ctx.options = 0 + self.assertEqual(0, ctx.options) + else: + with self.assertRaises(ValueError): + ctx.options = 0 + + @unittest.skip("Not yet supported on Jython") + def test_load_dh_params(self): + None + + def test_cert_store_stats(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.cert_store_stats(), + {'x509_ca': 0, 'crl': 0, 'x509': 0}) + # Jython x509 will grow by 1 while openssl remains 0 + # TODO investgate deeper + ctx.load_cert_chain(CERTFILE) + self.assertEqual(ctx.cert_store_stats(), + {'x509_ca': 0, 'crl': 0, 'x509': 1}) + ctx.load_verify_locations(CERTFILE) + self.assertEqual(ctx.cert_store_stats(), + {'x509_ca': 0, 'crl': 0, 'x509': 2}) + ctx.load_verify_locations(CAFILE_CACERT) + self.assertEqual(ctx.cert_store_stats(), + {'x509_ca': 1, 'crl': 0, 'x509': 2}) + + @unittest.skipIf(sys.platform == "win32", "not-Windows specific") + def test_load_default_certs_env(self): + # Store behaviour differs from CPython so different stats + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + with support.EnvironmentVarGuard() as env: + env["SSL_CERT_DIR"] = CAPATH + env["SSL_CERT_FILE"] = CERTFILE + ctx.load_default_certs() + self.assertEqual(ctx.cert_store_stats(), {"crl": 0, "x509": 5, "x509_ca": 0}) + + def _assert_context_options(self, ctx): + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + # Jython doesn't support OP_NO_COMPRESSION, OP_SINGLE_DH_USE + # OP_SINGLE_ECDH_USE, OP_CIPHER_SERVER_PREFERENCE + + @unittest.skip("Jython not using ssl.__https_verify_certificates ") + def test__https_verify_certificates(self): + None + + @unittest.skip("Jython not using ssl._https_verify_envvar ") + def test__https_verify_envvar(self): + None + + +class SSLErrorTests(test.test_ssl.SSLErrorTests): + def test_str(self): + # Different error strings for Jython + # The str() of a SSLError doesn't include the errno + e = ssl.SSLError(1, "foo") + self.assertIn("foo", str(e)) + self.assertEqual(e.errno, 1) + # Same for a subclass + e = ssl.SSLZeroReturnError(1, "foo") + self.assertIn("foo", str(e)) + self.assertEqual(e.errno, 1) + + @unittest.skip("Jython TODO") + def test_lib_reason(self): + None + + @unittest.skip("Jython TODO") + def test_subclass(self): + None + + +class NetworkedTests(test.test_ssl.NetworkedTests): + def test_connect_ex(self): + # Issue #11326: check connect_ex() implementation + with support.transient_internet(REMOTE_HOST): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=REMOTE_ROOT_CERT) + try: + # Jython, errno.EISCONN expected per earlier 2.x versions, not 0 + self.assertEqual(errno.EISCONN, s.connect_ex((REMOTE_HOST, 443))) + self.assertTrue(s.getpeercert()) + finally: + s.close() + + def test_non_blocking_connect_ex(self): + # Issue #11326: non-blocking connect_ex() should allow handshake + # to proceed after the socket gets ready. + # Jython behaviour varies + with support.transient_internet(REMOTE_HOST): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=REMOTE_ROOT_CERT, + do_handshake_on_connect=False) + try: + s.setblocking(False) + rc = s.connect_ex((REMOTE_HOST, 443)) + # EWOULDBLOCK under Windows, EINPROGRESS elsewhere + # Jython added EALREADY, as in Jython connect may have already happened + self.assertIn(rc, (0, errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK)) + # Wait for connect to finish + select.select([], [s], [], 5.0) + # Non-blocking handshake + while True: + try: + s.do_handshake() + break + except ssl.SSLWantReadError: + select.select([s], [], [], 5.0) + except ssl.SSLWantWriteError: + select.select([], [s], [], 5.0) + # SSL established - not in Jython + #self.assertTrue(s.getpeercert()) + finally: + s.close() + + def test_timeout_connect_ex(self): + # Issue #12065: on a timeout, connect_ex() should return the original + # errno (mimicking the behaviour of non-SSL sockets). + # Jython follows earlier 2.x behaviour of errno.EISCONN + # it also allows errno.TIMEDOUT + with support.transient_internet(REMOTE_HOST): + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=REMOTE_ROOT_CERT, + do_handshake_on_connect=False) + try: + s.settimeout(0.0000001) + rc = s.connect_ex((REMOTE_HOST, 443)) + if rc == errno.EISCONN: + self.skipTest("REMOTE_HOST responded too quickly") + self.assertIn(rc, (errno.ETIMEDOUT, errno.EAGAIN, errno.EWOULDBLOCK)) + finally: + s.close() + + @unittest.skip("Can't use a socket as a file under Jython") + def test_makefile_close(self): + None + + @unittest.skip("Currently not supported") + def test_ciphers(self): + None + + @unittest.skip("On jython preloaded TODO") + def test_get_ca_certs_capath(self): + None + + +def test_main(verbose=False): + tests=[ContextTests, BasicTests, BasicSocketTests, SSLErrorTests] + if support.is_resource_enabled('network'): + tests.append(NetworkedTests) + # Jython skip threading tests for now, really don't work :( + test.test_ssl.test_main(verbose, tests) + + +if __name__ == "__main__": + test_main() + diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ Development tip Bugs fixed + - [ 2774 ] test_ssl failures from weak certificate - [ 2768 ] Allow bytearray + and += to accept buffer protocol objects - [ 2742 ] JARs for bouncycastle out of date (upgrade to 1.16) - [ 2762 ] Upgrade Apache commons-compress to 1.18 diff --git a/lib-python/2.7/test/selfsigned_pythontestdotnet.pem b/lib-python/2.7/test/selfsigned_pythontestdotnet.pem new file mode 100644 --- /dev/null +++ b/lib-python/2.7/test/selfsigned_pythontestdotnet.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9zCCA9+gAwIBAgIUH98b4Fw/DyugC9cV7VK7ZODzHsIwDQYJKoZIhvcNAQEL +BQAwgYoxCzAJBgNVBAYTAlhZMRcwFQYDVQQIDA5DYXN0bGUgQW50aHJheDEYMBYG +A1UEBwwPQXJndW1lbnQgQ2xpbmljMSMwIQYDVQQKDBpQeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0aG9udGVzdC5uZXQw +HhcNMTkwNTA4MDEwMjQzWhcNMjcwNzI0MDEwMjQzWjCBijELMAkGA1UEBhMCWFkx +FzAVBgNVBAgMDkNhc3RsZSBBbnRocmF4MRgwFgYDVQQHDA9Bcmd1bWVudCBDbGlu +aWMxIzAhBgNVBAoMGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMSMwIQYDVQQD +DBpzZWxmLXNpZ25lZC5weXRob250ZXN0Lm5ldDCCAiIwDQYJKoZIhvcNAQEBBQAD +ggIPADCCAgoCggIBAMKdJlyCThkahwoBb7pl5q64Pe9Fn5jrIvzsveHTc97TpjV2 +RLfICnXKrltPk/ohkVl6K5SUZQZwMVzFubkyxE0nZPHYHlpiKWQxbsYVkYv01rix +IFdLvaxxbGYke2jwQao31s4o61AdlsfK1SdpHQUynBBMssqI3SB4XPmcA7e+wEEx +jxjVish4ixA1vuIZOx8yibu+CFCf/geEjoBMF3QPdzULzlrCSw8k/45iZCSoNbvK +DoL4TVV07PHOxpheDh8ZQmepGvU6pVqhb9m4lgmV0OGWHgozd5Ur9CbTVDmxIEz3 +TSoRtNJK7qtyZdGNqwjksQxgZTjM/d/Lm/BJG99AiOmYOjsl9gbQMZgvQmMAtUsI +aMJnQuZ6R+KEpW/TR5qSKLWZSG45z/op+tzI2m+cE6HwTRVAWbcuJxcAA55MZjqU +OOOu3BBYMjS5nf2sQ9uoXsVBFH7i0mQqoW1SLzr9opI8KsWwFxQmO2vBxWYaN+lH +OmwBZBwyODIsmI1YGXmTp09NxRYz3Qe5GCgFzYowpMrcxUC24iduIdMwwhRM7rKg +7GtIWMSrFfuI1XCLRmSlhDbhNN6fVg2f8Bo9PdH9ihiIyxSrc+FOUasUYCCJvlSZ +8hFUlLvcmrZlWuazohm0lsXuMK1JflmQr/DA/uXxP9xzFfRy+RU3jDyxJbRHAgMB +AAGjUzBRMB0GA1UdDgQWBBSQJyxiPMRK01i+0BsV9zUwDiBaHzAfBgNVHSMEGDAW +gBSQJyxiPMRK01i+0BsV9zUwDiBaHzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 +DQEBCwUAA4ICAQCR+7a7N/m+WLkxPPIA/CB4MOr2Uf8ixTv435Nyv6rXOun0+lTP +ExSZ0uYQ+L0WylItI3cQHULldDueD+s8TGzxf5woaLKf6tqyr0NYhKs+UeNEzDnN +9PHQIhX0SZw3XyXGUgPNBfRCg2ZDdtMMdOU4XlQN/IN/9hbYTrueyY7eXq9hmtI9 +1srftAMqr9SR1JP7aHI6DVgrEsZVMTDnfT8WmLSGLlY1HmGfdEn1Ip5sbo9uSkiH +AEPgPfjYIvR5LqTOMn4KsrlZyBbFIDh9Sl99M1kZzgH6zUGVLCDg1y6Cms69fx/e +W1HoIeVkY4b4TY7Bk7JsqyNhIuqu7ARaxkdaZWhYaA2YyknwANdFfNpfH+elCLIk +BUt5S3f4i7DaUePTvKukCZiCq4Oyln7RcOn5If73wCeLB/ZM9Ei1HforyLWP1CN8 +XLfpHaoeoPSWIveI0XHUl65LsPN2UbMbul/F23hwl+h8+BLmyAS680Yhn4zEN6Ku +B7Po90HoFa1Du3bmx4jsN73UkT/dwMTi6K072FbipnC1904oGlWmLwvAHvrtxxmL +Pl3pvEaZIu8wa/PNF6Y7J7VIewikIJq6Ta6FrWeFfzMWOj2qA1ZZi6fUaDSNYvuV +J5quYKCc/O+I/yDDf8wyBbZ/gvUXzUHTMYGG+bFrn1p7XDbYYeEJ6R/xEg== +-----END CERTIFICATE----- -- Repository URL: https://hg.python.org/jython From jython-checkins at python.org Fri Jun 28 04:20:19 2019 From: jython-checkins at python.org (jeff.allen) Date: Fri, 28 Jun 2019 08:20:19 +0000 Subject: [Jython-checkins] =?utf-8?q?jython=3A_Update_Gradle_to_4=2E10_an?= =?utf-8?q?d_make_minor_tweaks_to_the_build=2E?= Message-ID: <20190628082019.1.246EC1F6BB7746AA@mg.python.org> https://hg.python.org/jython/rev/c3f584b2e220 changeset: 8251:c3f584b2e220 user: Jeff Allen date: Mon Jun 24 06:45:30 2019 +0100 summary: Update Gradle to 4.10 and make minor tweaks to the build. files: build.gradle | 163 +++++----- gradle/wrapper/gradle-wrapper.jar | Bin gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 86 insertions(+), 79 deletions(-) diff --git a/build.gradle b/build.gradle --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,7 @@ * here. But of course you have to do it the hard way too (see build.xml) as * long as Ant is also used. */ +// Versions are specified in this grammar: // . ( . )? ( )? ([-+] ? )? version = '2.7.2a1+' @@ -127,63 +128,67 @@ dependencies { /* * Must these correspond exactly with the external libraries (JARs) - * mentioned in the Ant build.xml, or is it better to allow "at least" - * matching of versions, to grant this freedom to user applications? + * mentioned in the Ant build.xml? Or is some form of dynamic version + * better for downstream? + * + * Note that an application may specify a later version. Gradle will + * choose the latest required. */ // Using a version available from repo (not 'extlibs/servlet-api-2.5' as in build.xml) - implementation group: 'javax.servlet', name: 'javax.servlet-api', version: '3.1.0' + implementation 'javax.servlet:javax.servlet-api:3.1.0' /* * These seem to be unnecessary while the proprietary database support is - * not bundled with Jython. Applications needing them can cite thse or a + * not bundled with Jython. Applications needing them can cite these or a * version they prefer. */ - //implementation group: 'mysql', name: 'mysql-connector-java', version: '5.1.42' - //implementation group: 'org.postgresql', name: 'postgresql', version: '42.1.1.jre7' + //implementation 'mysql:mysql-connector-java:5.1.42' + //implementation 'org.postgresql:postgresql:42.1.1.jre7' - // pin to Antlr 3.1.3 until we upgrade parsing - antlr 'org.antlr:antlr:3.1.3' // use ANTLR version 3 - implementation 'org.antlr:antlr-runtime:3.1.3' - implementation 'org.antlr:stringtemplate:3.2.1' + // pin to Antlr 3 until we upgrade parsing + antlr 'org.antlr:antlr:3.5.2' + implementation 'org.antlr:antlr-runtime:3.5.2' - implementation group: 'org.apache.commons', name: 'commons-compress', version: '1.18' + implementation 'org.apache.commons:commons-compress:1.18' - implementation group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.61' - implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.61' + implementation 'org.bouncycastle:bcpkix-jdk15on:1.61' + implementation 'org.bouncycastle:bcprov-jdk15on:1.61' - implementation group: 'org.ow2.asm', name: 'asm', version: '7.0' - implementation group: 'org.ow2.asm', name: 'asm-commons', version: '7.0' - implementation group: 'org.ow2.asm', name: 'asm-util', version: '7.0' + implementation 'org.ow2.asm:asm:7.0' + implementation 'org.ow2.asm:asm-commons:7.0' + implementation 'org.ow2.asm:asm-util:7.0' - api group: 'com.google.guava', name: 'guava', version: '27.1-android' - compile group: 'com.google.guava', name: 'failureaccess', version: '1.0.1' + // The Android Guava and "failureaccess" are necessary to support Java 7. + implementation 'com.google.guava:guava:27.1-android' + implementation 'com.google.guava:failureaccess:1.0.1' + // Swap for regular Guava at Java 8. - implementation group: 'com.ibm.icu', name: 'icu4j', version: '59.1' + implementation 'com.ibm.icu:icu4j:59.1' - implementation group: 'com.carrotsearch', name: 'java-sizeof', version: '0.0.5' + implementation 'com.carrotsearch:java-sizeof:0.0.5' - implementation group: 'com.github.jnr', name: 'jffi', version: '1.2.18' - implementation group: 'com.github.jnr', name: 'jnr-netdb', version: '1.1.6' - implementation group: 'com.github.jnr', name: 'jnr-ffi', version: '2.1.9' - implementation group: 'com.github.jnr', name: 'jnr-posix', version: '3.0.49' - implementation group: 'com.github.jnr', name: 'jnr-constants', version: '0.9.12' + implementation 'com.github.jnr:jffi:1.2.18' + implementation 'com.github.jnr:jnr-netdb:1.1.6' + implementation 'com.github.jnr:jnr-ffi:2.1.9' + implementation 'com.github.jnr:jnr-posix:3.0.49' + implementation 'com.github.jnr:jnr-constants:0.9.12' - implementation group: 'jline', name: 'jline', version: '2.14.5' + implementation 'jline:jline:2.14.5' - implementation group: 'io.netty', name: 'netty-buffer', version: '4.1.24.Final' - implementation group: 'io.netty', name: 'netty-codec', version: '4.1.24.Final' - implementation group: 'io.netty', name: 'netty-common', version: '4.1.24.Final' - implementation group: 'io.netty', name: 'netty-handler', version: '4.1.24.Final' - implementation group: 'io.netty', name: 'netty-resolver', version: '4.1.24.Final' - implementation group: 'io.netty', name: 'netty-transport', version: '4.1.24.Final' + implementation 'io.netty:netty-buffer:4.1.24.Final' + implementation 'io.netty:netty-codec:4.1.24.Final' + implementation 'io.netty:netty-common:4.1.24.Final' + implementation 'io.netty:netty-handler:4.1.24.Final' + implementation 'io.netty:netty-resolver:4.1.24.Final' + implementation 'io.netty:netty-transport:4.1.24.Final' // Used implicitly in the Ant build, must be explicit here - implementation group: 'org.apache.ant', name: 'ant', version: '1.9.7' + implementation 'org.apache.ant:ant:1.9.7' - testImplementation group: 'junit', name: 'junit', version: '4.10' + testImplementation 'junit:junit:4.10' +} -} // ---------------- Resource Processing ---------------------------------------- @@ -321,7 +326,7 @@ /* * Decompose the version string into elements for Jython to access as * properties. (The Ant build.xml requires them to be set in parts, but - * we can work it out from the .) + * we can work it out from project.version.) */ // .(.)()?([-+])? def versionRegex = /(\d+)\.(\d+)(\.(\d+))?((a|b|rc)(\d+))?([-+]\w*)?/ @@ -342,7 +347,7 @@ int SNAPSHOT = 0xaa int level = 0, serial = 0 if (versionResult[5]) { - // This is some kind of pre-final release (unless snapshaot) + // This is some kind of pre-final release (unless snapshot) serial = versionResult[7] as int switch (versionResult[6]) { case 'a': level = 0xa; break // ALPHA release @@ -554,12 +559,12 @@ doLast { /* - * Now use the 'jycompile' Ant task tocompile the Python source we - * supply to users. The exclusions are copied from build.xml, as also - * is this comment: + * Now use the 'jycompile' Ant task to compile the Python source we + * supply to users. The exclusions have been copied from build.xml, + * and also this comment: