From fa3ac95df5bdcd66fe8f8ea645677b73b6c53a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Thu, 24 Mar 2022 21:37:39 -0400 Subject: [PATCH 1/5] Enable some ssl tests --- .../Cases/CPythonCasesManifest.ini | 2 +- Tests/test_ssl_stdlib.py | 124 ++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 Tests/test_ssl_stdlib.py diff --git a/Src/IronPythonTest/Cases/CPythonCasesManifest.ini b/Src/IronPythonTest/Cases/CPythonCasesManifest.ini index aa2ed6f05..cb9ecbe58 100644 --- a/Src/IronPythonTest/Cases/CPythonCasesManifest.ini +++ b/Src/IronPythonTest/Cases/CPythonCasesManifest.ini @@ -846,7 +846,7 @@ RunCondition=$(IS_POSIX) [CPython.test_sqlite] Ignore=true -[CPython.test_ssl] +[CPython.test_ssl] # IronPython.test_ssl_stdlib Ignore=true Reason=Blocking diff --git a/Tests/test_ssl_stdlib.py b/Tests/test_ssl_stdlib.py new file mode 100644 index 000000000..528246d08 --- /dev/null +++ b/Tests/test_ssl_stdlib.py @@ -0,0 +1,124 @@ +# Licensed to the .NET Foundation under one or more agreements. +# The .NET Foundation licenses this file to you under the Apache 2.0 License. +# See the LICENSE file in the project root for more information. + +## +## Run selected tests from test_ssl from StdLib +## + +import unittest +import sys + +from iptest import run_test + +import test.test_ssl + +def load_tests(loader, standard_tests, pattern): + if sys.implementation.name == 'ironpython': + suite = unittest.TestSuite() + suite.addTest(test.test_ssl.BasicSocketTests('test_DER_to_PEM')) + suite.addTest(test.test_ssl.BasicSocketTests('test_asn1object')) + suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_constants'))) # AttributeError: 'module' object has no attribute 'OP_CIPHER_SERVER_PREFERENCE' + suite.addTest(test.test_ssl.BasicSocketTests('test_dealloc_warn')) + suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_enum_certificates'))) # AssertionError: OSError not raised by enum_certificates + suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_enum_crls'))) # AssertionError: [] is not true + suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_errors'))) # AssertionError: OSError not raised + suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_get_default_verify_paths'))) # AttributeError: 'module' object has no attribute 'get_default_verify_paths' + suite.addTest(test.test_ssl.BasicSocketTests('test_match_hostname')) + suite.addTest(test.test_ssl.BasicSocketTests('test_openssl_version')) + suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_parse_cert'))) # KeyError: OCSP + suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_parse_cert_CVE_2013_4238'))) # AssertionError: Tuples differ + suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_parse_cert_CVE_2019_5010'))) # AssertionError + suite.addTest(test.test_ssl.BasicSocketTests('test_purpose_enum')) + suite.addTest(test.test_ssl.BasicSocketTests('test_random')) + suite.addTest(test.test_ssl.BasicSocketTests('test_random_fork')) + suite.addTest(test.test_ssl.BasicSocketTests('test_refcycle')) + suite.addTest(test.test_ssl.BasicSocketTests('test_server_side')) + suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_timeout'))) # AssertionError: 0.0 != None + suite.addTest(test.test_ssl.BasicSocketTests('test_tls_unique_channel_binding')) + suite.addTest(test.test_ssl.BasicSocketTests('test_unknown_channel_binding')) + suite.addTest(test.test_ssl.BasicSocketTests('test_unsupported_dtls')) + suite.addTest(test.test_ssl.BasicSocketTests('test_wrapped_unconnected')) + suite.addTest(test.test_ssl.ContextTests('test__create_stdlib_context')) + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_cert_store_stats'))) # AttributeError: 'SSLContext' object has no attribute 'cert_store_stats' + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_check_hostname'))) # AssertionError: ValueError not raised + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_ciphers'))) # AssertionError: SSLError not raised + suite.addTest(test.test_ssl.ContextTests('test_constructor')) + suite.addTest(test.test_ssl.ContextTests('test_create_default_context')) + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_get_ca_certs'))) # AttributeError: 'SSLContext' object has no attribute 'get_ca_certs' + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_cert_chain'))) # AssertionError: OSError not raised + suite.addTest(test.test_ssl.ContextTests('test_load_default_certs')) + suite.addTest(test.test_ssl.ContextTests('test_load_default_certs_env')) + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_default_certs_env_windows'))) # AttributeError: 'SSLContext' object has no attribute 'cert_store_stats' + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_dh_params'))) # AttributeError: 'SSLContext' object has no attribute 'load_dh_params' + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_verify_cadata'))) # AttributeError: 'SSLContext' object has no attribute 'cert_store_stats' + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_verify_locations'))) # TypeError: expected str, got bytes + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_options'))) # AssertionError: 2197818367 != 50331648 + suite.addTest(test.test_ssl.ContextTests('test_protocol')) + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_session_stats'))) # AttributeError: 'SSLContext' object has no attribute 'session_stats' + suite.addTest(test.test_ssl.ContextTests('test_set_default_verify_paths')) + suite.addTest(test.test_ssl.ContextTests('test_set_ecdh_curve')) + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_sni_callback'))) # AttributeError: 'SSLContext' object has no attribute 'set_servername_callback' + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_sni_callback_refcycle'))) # AttributeError: 'SSLContext' object has no attribute 'set_servername_callback' + suite.addTest(test.test_ssl.ContextTests('test_verify_flags')) + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_verify_mode'))) # AssertionError: ValueError not raised + suite.addTest(test.test_ssl.NetworkedTests('test_algorithms')) + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_ciphers'))) # AssertionError: SSLError not raised + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_connect'))) # AssertionError: {} != None + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_connect_cadata'))) # ssl.SSLError: [Errno 'errors while validating certificate chain: '] RemoteCertificateChainErrors + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_connect_capath'))) # ssl.SSLError: [Errno 'errors while validating certificate chain: '] RemoteCertificateChainErrors + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_connect_ex'))) # ssl.SSLError: [Errno 'errors while validating certificate chain: '] RemoteCertificateChainErrors + #suite.addTest(test.test_ssl.NetworkedTests('test_connect_ex_error')) # slow + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_connect_with_context'))) # AssertionError: {} != None + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_context_setget'))) # AttributeError: '_SSLSocket' object has no attribute 'context' + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_get_ca_certs_capath'))) # AttributeError: 'SSLContext' object has no attribute 'get_ca_certs' + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_get_server_certificate'))) # TypeError: Value cannot be null. + suite.addTest(test.test_ssl.NetworkedTests('test_makefile_close')) + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_non_blocking_connect_ex'))) # OSError: [Errno -2146232800] The operation is not allowed on a non-blocking Socket. + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_non_blocking_handshake'))) # TypeError: Value cannot be null. + suite.addTest(test.test_ssl.NetworkedTests('test_timeout_connect_ex')) + suite.addTest(unittest.expectedFailure(test.test_ssl.SSLErrorTests('test_lib_reason'))) # AttributeError: 'SSLContext' object has no attribute 'load_dh_params' + suite.addTest(unittest.expectedFailure(test.test_ssl.SSLErrorTests('test_str'))) # AssertionError: '[Errno 1] foo' != 'foo' + suite.addTest(unittest.expectedFailure(test.test_ssl.SSLErrorTests('test_subclass'))) # TypeError: Value cannot be null. + #suite.addTest(test.test_ssl.ThreadedTests('test_asyncore_server')) + #suite.addTest(test.test_ssl.ThreadedTests('test_check_hostname')) + #suite.addTest(test.test_ssl.ThreadedTests('test_compression')) + #suite.addTest(test.test_ssl.ThreadedTests('test_compression_disabled')) + #suite.addTest(test.test_ssl.ThreadedTests('test_crl_check')) + #suite.addTest(test.test_ssl.ThreadedTests('test_default_ciphers')) + #suite.addTest(test.test_ssl.ThreadedTests('test_default_ecdh_curve')) + #suite.addTest(test.test_ssl.ThreadedTests('test_dh_params')) + #suite.addTest(test.test_ssl.ThreadedTests('test_do_handshake_enotconn')) + #suite.addTest(test.test_ssl.ThreadedTests('test_echo')) + #suite.addTest(test.test_ssl.ThreadedTests('test_empty_cert')) + #suite.addTest(test.test_ssl.ThreadedTests('test_getpeercert')) + #suite.addTest(test.test_ssl.ThreadedTests('test_getpeercert_enotconn')) + #suite.addTest(test.test_ssl.ThreadedTests('test_handshake_timeout')) + #suite.addTest(test.test_ssl.ThreadedTests('test_malformed_cert')) + #suite.addTest(test.test_ssl.ThreadedTests('test_malformed_key')) + #suite.addTest(test.test_ssl.ThreadedTests('test_nonexisting_cert')) + #suite.addTest(test.test_ssl.ThreadedTests('test_npn_protocols')) + #suite.addTest(test.test_ssl.ThreadedTests('test_protocol_sslv2')) + #suite.addTest(test.test_ssl.ThreadedTests('test_protocol_sslv23')) + #suite.addTest(test.test_ssl.ThreadedTests('test_protocol_sslv3')) + #suite.addTest(test.test_ssl.ThreadedTests('test_protocol_tlsv1')) + #suite.addTest(test.test_ssl.ThreadedTests('test_protocol_tlsv1_1')) + #suite.addTest(test.test_ssl.ThreadedTests('test_protocol_tlsv1_2')) + #suite.addTest(test.test_ssl.ThreadedTests('test_read_write_after_close_raises_valuerror')) + #suite.addTest(test.test_ssl.ThreadedTests('test_recv_send')) + #suite.addTest(test.test_ssl.ThreadedTests('test_rude_shutdown')) + #suite.addTest(test.test_ssl.ThreadedTests('test_selected_npn_protocol')) + #suite.addTest(test.test_ssl.ThreadedTests('test_server_accept')) + #suite.addTest(test.test_ssl.ThreadedTests('test_sni_callback')) + #suite.addTest(test.test_ssl.ThreadedTests('test_sni_callback_alert')) + #suite.addTest(test.test_ssl.ThreadedTests('test_sni_callback_raising')) + #suite.addTest(test.test_ssl.ThreadedTests('test_sni_callback_wrong_return_type')) + #suite.addTest(test.test_ssl.ThreadedTests('test_socketserver')) + #suite.addTest(test.test_ssl.ThreadedTests('test_starttls')) + #suite.addTest(test.test_ssl.ThreadedTests('test_tls_unique_channel_binding')) + return suite + + else: + return loader.loadTestsFromModule(test.test_ssl, pattern) + +run_test(__name__) From d42c98e3a63d28fbfaa10cd098c62ef401db2070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Fri, 25 Mar 2022 08:32:10 -0400 Subject: [PATCH 2/5] Disable failing tests on Linux --- Tests/test_ssl_stdlib.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Tests/test_ssl_stdlib.py b/Tests/test_ssl_stdlib.py index 528246d08..42086de50 100644 --- a/Tests/test_ssl_stdlib.py +++ b/Tests/test_ssl_stdlib.py @@ -9,7 +9,7 @@ import unittest import sys -from iptest import run_test +from iptest import is_posix, run_test import test.test_ssl @@ -48,7 +48,10 @@ def load_tests(loader, standard_tests, pattern): suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_get_ca_certs'))) # AttributeError: 'SSLContext' object has no attribute 'get_ca_certs' suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_cert_chain'))) # AssertionError: OSError not raised suite.addTest(test.test_ssl.ContextTests('test_load_default_certs')) - suite.addTest(test.test_ssl.ContextTests('test_load_default_certs_env')) + if is_posix: + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_default_certs_env'))) # 'SSLContext' object has no attribute 'cert_store_stats' + else: + suite.addTest(test.test_ssl.ContextTests('test_load_default_certs_env')) suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_default_certs_env_windows'))) # AttributeError: 'SSLContext' object has no attribute 'cert_store_stats' suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_dh_params'))) # AttributeError: 'SSLContext' object has no attribute 'load_dh_params' suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_verify_cadata'))) # AttributeError: 'SSLContext' object has no attribute 'cert_store_stats' @@ -73,7 +76,10 @@ def load_tests(loader, standard_tests, pattern): suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_context_setget'))) # AttributeError: '_SSLSocket' object has no attribute 'context' suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_get_ca_certs_capath'))) # AttributeError: 'SSLContext' object has no attribute 'get_ca_certs' suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_get_server_certificate'))) # TypeError: Value cannot be null. - suite.addTest(test.test_ssl.NetworkedTests('test_makefile_close')) + if is_posix: + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_makefile_close'))) # OSError: [Errno 9] Bad file descriptor + else: + suite.addTest(test.test_ssl.NetworkedTests('test_makefile_close')) suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_non_blocking_connect_ex'))) # OSError: [Errno -2146232800] The operation is not allowed on a non-blocking Socket. suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_non_blocking_handshake'))) # TypeError: Value cannot be null. suite.addTest(test.test_ssl.NetworkedTests('test_timeout_connect_ex')) From d20bdc97ca68e418d10d3249ca17ef91a9dba30e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Sat, 26 Mar 2022 13:11:03 -0400 Subject: [PATCH 3/5] Fix some tests and _SSLSocket cleanup --- Src/IronPython.Modules/_ssl.cs | 100 ++++++++++----------- Src/StdLib/Lib/test/test_ssl.py | 6 +- Tests/modules/network_related/test__ssl.py | 32 +++++-- Tests/test_ssl_stdlib.py | 14 +-- 4 files changed, 83 insertions(+), 69 deletions(-) diff --git a/Src/IronPython.Modules/_ssl.cs b/Src/IronPython.Modules/_ssl.cs index 7a2b1deeb..63af45db8 100644 --- a/Src/IronPython.Modules/_ssl.cs +++ b/Src/IronPython.Modules/_ssl.cs @@ -109,17 +109,19 @@ public static void RAND_add(object buf, double entropy) { [PythonType] public class _SSLContext { - private readonly X509Certificate2Collection _cert_store = new X509Certificate2Collection(); - private string _cafile; + internal readonly X509Certificate2Collection _cert_store = new X509Certificate2Collection(); + internal string _cafile; private int _verify_mode = SSL_VERIFY_NONE; - public _SSLContext(CodeContext context, int protocol = PROTOCOL_SSLv23) { + public _SSLContext(CodeContext context, int protocol) { if (protocol != PROTOCOL_SSLv2 && protocol != PROTOCOL_SSLv23 && protocol != PROTOCOL_SSLv3 && protocol != PROTOCOL_TLSv1 && protocol != PROTOCOL_TLSv1_1 && protocol != PROTOCOL_TLSv1_2) { throw PythonOps.ValueError("invalid protocol version"); } this.protocol = protocol; + + options = OP_ALL; if (protocol != PROTOCOL_SSLv2) options |= OP_NO_SSLv2; if (protocol != PROTOCOL_SSLv3) @@ -175,14 +177,20 @@ public void load_cert_chain(string certfile, string keyfile = null, object passw } - public void load_verify_locations(CodeContext context, string cafile = null, string capath = null, object cadata = null) { + public void load_verify_locations(CodeContext context, object cafile = null, string capath = null, object cadata = null) { if (cafile == null && capath == null && cadata == null) { throw PythonOps.TypeError("cafile, capath and cadata cannot be all omitted"); } - if (cafile != null) { - _cert_store.Add(ReadCertificate(context, cafile)); - _cafile = cafile; + if (cafile is not null) { + if (cafile is string s) { + _cafile = s; + } else if (cafile is Bytes b) { + _cafile = b.MakeString(); + } else { + throw PythonOps.TypeError("cafile should be a valid filesystem path"); + } + _cert_store.Add(ReadCertificate(context, _cafile)); } if (capath != null) { @@ -207,8 +215,8 @@ public void load_verify_locations(CodeContext context, string cafile = null, str } } - public object _wrap_socket(CodeContext context, PythonSocket.socket sock, bool server_side, string server_hostname = null, object ssl_sock = null) { - return new _SSLSocket(context, sock, server_side, null, _cafile, verify_mode, protocol | options, null, _cert_store) { _serverHostName = server_hostname }; + public object _wrap_socket(CodeContext context, PythonSocket.socket sock, bool server_side, string server_hostname = null) { + return new _SSLSocket(context, this, sock, server_side, server_hostname); } } @@ -225,34 +233,22 @@ public class _SSLSocket { private Exception _validationFailure; internal string _serverHostName; - public _SSLSocket(CodeContext context, PythonSocket.socket sock, string keyfile = null, string certfile = null, X509Certificate2Collection certs = null) { - _context = context; - _sslStream = new SslStream(new NetworkStream(sock._socket, false), true, CertValidationCallback); - _socket = sock; - _protocol = PythonSsl.PROTOCOL_SSLv23 | PythonSsl.OP_NO_SSLv2 | PythonSsl.OP_NO_SSLv3; - _validate = false; - _certCollection = certs ?? new X509Certificate2Collection(); - } + public _SSLContext context { get; } - internal _SSLSocket(CodeContext context, - PythonSocket.socket sock, - bool server_side, - string keyfile = null, - string certfile = null, - int certs_mode = PythonSsl.CERT_NONE, - int protocol = (PythonSsl.PROTOCOL_SSLv23 | PythonSsl.OP_NO_SSLv2 | PythonSsl.OP_NO_SSLv3), - string cacertsfile = null, - X509Certificate2Collection certs = null) { + internal _SSLSocket(CodeContext context, _SSLContext sslcontext, PythonSocket.socket sock, bool server_side, string server_hostname) { if (sock == null) { throw PythonOps.TypeError("expected socket object, got None"); } + this.context = sslcontext; _serverSide = server_side; - bool validate; - _certsMode = certs_mode; + _serverHostName = server_hostname; + + _certsMode = sslcontext.verify_mode; + bool validate; RemoteCertificateValidationCallback callback; - switch (certs_mode) { + switch (_certsMode) { case PythonSsl.CERT_NONE: validate = false; callback = CertValidationCallback; @@ -266,28 +262,24 @@ internal _SSLSocket(CodeContext context, callback = CertValidationCallbackRequired; break; default: - throw new InvalidOperationException(String.Format("bad certs_mode: {0}", certs_mode)); + throw new InvalidOperationException(String.Format("bad certs_mode: {0}", _certsMode)); } _callback = callback; - if (certs != null) { - _certCollection = certs; + if (sslcontext._cert_store != null) { + _certCollection = sslcontext._cert_store; } - if (certfile != null) { - _cert = PythonSsl.ReadCertificate(context, certfile); - } - - if (cacertsfile != null) { - _certCollection = new X509Certificate2Collection(new[] { PythonSsl.ReadCertificate(context, cacertsfile) }); + if (sslcontext._cafile != null) { + _cert = PythonSsl.ReadCertificate(context, sslcontext._cafile); } _socket = sock; EnsureSslStream(false); - _protocol = protocol; + _protocol = sslcontext.protocol; _validate = validate; _context = context; } @@ -422,7 +414,7 @@ public void do_handshake() { if (_cert != null) { collection.Add(_cert); } - _sslStream.AuthenticateAsClient(_serverHostName ?? _socket._hostName, collection, enabledSslProtocols, false); + _sslStream.AuthenticateAsClient(_serverHostName ?? _socket._hostName ?? string.Empty, collection, enabledSslProtocols, false); } } catch (AuthenticationException e) { ((IDisposable)_socket._socket).Dispose(); @@ -521,7 +513,7 @@ public object peer_certificate(bool binary_form) { if (peerCert != null) { if (binary_form) { - return peerCert.GetRawCertData().MakeString(); + return Bytes.Make(peerCert.GetRawCertData()); } else if (_validate) { return CertificateToPython(_context, peerCert); } @@ -548,24 +540,23 @@ public string issuer() { return String.Empty; } - [Documentation(@"read([len]) -> string - -Read up to len bytes from the SSL socket.")] - public object read(CodeContext/*!*/ context, int len, ByteArray buffer = null) { + [Documentation(@"read(size, [buffer]) +Read up to size bytes from the SSL socket.")] + public object read(CodeContext/*!*/ context, int size, ByteArray buffer = null) { EnsureSslStream(true); try { byte[] buf = new byte[2048]; - MemoryStream result = new MemoryStream(len); + MemoryStream result = new MemoryStream(size); while (true) { - int readLength = (len < buf.Length) ? len : buf.Length; + int readLength = (size < buf.Length) ? size : buf.Length; int bytes = _sslStream.Read(buf, 0, readLength); if (bytes > 0) { result.Write(buf, 0, bytes); - len -= bytes; + size -= bytes; } - if (bytes == 0 || len == 0 || bytes < readLength) { + if (bytes == 0 || size == 0 || bytes < readLength) { var res = result.ToArray(); if (buffer == null) return Bytes.Make(res); @@ -593,10 +584,9 @@ public string server() { return String.Empty; } - [Documentation(@"write(s) -> len + [Documentation(@"Writes the bytes-like object b into the SSL object. -Writes the string s into the SSL object. Returns the number -of bytes written.")] +Returns the number of bytes written.")] public int write(CodeContext/*!*/ context, Bytes data) { EnsureSslStream(true); @@ -1046,8 +1036,10 @@ private static Exception ErrorDecoding(CodeContext context, params object[] args public const int PROTOCOL_TLSv1_1 = 4; public const int PROTOCOL_TLSv1_2 = 5; - public const uint OP_ALL = 0x80000BFF; - public const uint OP_DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800; + public const int OP_ALL = unchecked((int)0x800003FF); + public const int OP_CIPHER_SERVER_PREFERENCE = 0x400000; + public const int OP_SINGLE_DH_USE = 0x100000; + public const int OP_SINGLE_ECDH_USE = 0x80000; public const int OP_NO_SSLv2 = 0x01000000; public const int OP_NO_SSLv3 = 0x02000000; public const int OP_NO_TLSv1 = 0x04000000; diff --git a/Src/StdLib/Lib/test/test_ssl.py b/Src/StdLib/Lib/test/test_ssl.py index 4fdb7f087..3798d1c03 100644 --- a/Src/StdLib/Lib/test/test_ssl.py +++ b/Src/StdLib/Lib/test/test_ssl.py @@ -76,6 +76,7 @@ def handle_error(prefix): sys.stdout.write(prefix + exc_format) def can_clear_options(): + if sys.implementation.name == 'ironpython': return True # 0.9.8m or higher return ssl._OPENSSL_API_VERSION >= (0, 9, 8, 13, 15) @@ -569,7 +570,10 @@ def test_enum_certificates(self): self.assertTrue(ssl.enum_certificates("ROOT")) self.assertRaises(TypeError, ssl.enum_certificates) - self.assertRaises(WindowsError, ssl.enum_certificates, "") + if sys.implementation.name == "ironpython": + self.assertEqual(ssl.enum_certificates(""), []) + else: + self.assertRaises(WindowsError, ssl.enum_certificates, "") trust_oids = set() for storename in ("CA", "ROOT"): diff --git a/Tests/modules/network_related/test__ssl.py b/Tests/modules/network_related/test__ssl.py index 0b6bb3185..e1ea0f3b2 100644 --- a/Tests/modules/network_related/test__ssl.py +++ b/Tests/modules/network_related/test__ssl.py @@ -9,6 +9,7 @@ import _ssl import os import socket +import sys import unittest from iptest import IronPythonTestCase, is_cli, is_netcoreapp, retryOnFailure, run_test, skipUnlessIronPython @@ -27,13 +28,22 @@ def test_constants(self): self.assertEqual(_ssl.CERT_NONE, 0) self.assertEqual(_ssl.CERT_OPTIONAL, 1) self.assertEqual(_ssl.CERT_REQUIRED, 2) - self.assertEqual(_ssl.PROTOCOL_SSLv2, 0) + if sys.version_info >= (3,5): + self.assertRaises(AttributeError, lambda: _ssl.PROTOCOL_SSLv2) + else: + self.assertEqual(_ssl.PROTOCOL_SSLv2, 0) self.assertEqual(_ssl.PROTOCOL_SSLv23, 2) - self.assertEqual(_ssl.PROTOCOL_SSLv3, 1) + if sys.version_info >= (3,7): + self.assertRaises(AttributeError, lambda: _ssl.PROTOCOL_SSLv3) + else: + self.assertEqual(_ssl.PROTOCOL_SSLv3, 1) self.assertEqual(_ssl.PROTOCOL_TLSv1, 3) self.assertEqual(_ssl.PROTOCOL_TLSv1_1, 4) self.assertEqual(_ssl.PROTOCOL_TLSv1_2, 5) - self.assertEqual(_ssl.OP_NO_SSLv2, 0x1000000) + if sys.version_info >= (3,7): + self.assertEqual(_ssl.OP_NO_SSLv2, 0) + else: + self.assertEqual(_ssl.OP_NO_SSLv2, 0x1000000) self.assertEqual(_ssl.OP_NO_SSLv3, 0x2000000) self.assertEqual(_ssl.OP_NO_TLSv1, 0x4000000) self.assertEqual(_ssl.OP_NO_TLSv1_1, 0x10000000) @@ -106,7 +116,8 @@ def test_SSLType_ssl(self): context = _ssl._SSLContext(_ssl.PROTOCOL_SSLv23) ssl_s = context._wrap_socket(s, False) - ssl_s.shutdown() + if is_cli: + ssl_s.shutdown() s.close() #sock, keyfile, certfile @@ -133,6 +144,7 @@ def test_SSLType_ssl_neg(self): #Cleanup s.close() + @skipUnlessIronPython() def test_SSLType_issuer(self): #--Positive s = socket.socket(socket.AF_INET) @@ -165,6 +177,7 @@ def test_SSLType_issuer(self): ssl_s.shutdown() s.close() + @skipUnlessIronPython() def test_SSLType_server(self): #--Positive s = socket.socket(socket.AF_INET) @@ -206,8 +219,12 @@ def test_SSLType_read_and_write(self): ssl_s = context._wrap_socket(s, False) ssl_s.do_handshake() - self.assertIn("Writes the string s into the SSL object.", ssl_s.write.__doc__) - self.assertIn("Read up to len bytes from the SSL socket.", ssl_s.read.__doc__) + if is_cli or sys.version_info >= (3,5): + self.assertIn("Writes the bytes-like object b into the SSL object.", ssl_s.write.__doc__) + self.assertIn("Read up to size bytes from the SSL socket.", ssl_s.read.__doc__) + else: + self.assertIn("Writes the string s into the SSL object.", ssl_s.write.__doc__) + self.assertIn("Read up to len bytes from the SSL socket.", ssl_s.read.__doc__) #Write self.assertEqual(ssl_s.write(SSL_REQUEST), @@ -225,7 +242,8 @@ def test_SSLType_read_and_write(self): self.assertIn(SSL_RESPONSE, response) #Cleanup - ssl_s.shutdown() + if is_cli: + ssl_s.shutdown() s.close() def test_parse_cert(self): diff --git a/Tests/test_ssl_stdlib.py b/Tests/test_ssl_stdlib.py index 42086de50..7ebb4cae9 100644 --- a/Tests/test_ssl_stdlib.py +++ b/Tests/test_ssl_stdlib.py @@ -18,9 +18,9 @@ def load_tests(loader, standard_tests, pattern): suite = unittest.TestSuite() suite.addTest(test.test_ssl.BasicSocketTests('test_DER_to_PEM')) suite.addTest(test.test_ssl.BasicSocketTests('test_asn1object')) - suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_constants'))) # AttributeError: 'module' object has no attribute 'OP_CIPHER_SERVER_PREFERENCE' + suite.addTest(test.test_ssl.BasicSocketTests('test_constants')) suite.addTest(test.test_ssl.BasicSocketTests('test_dealloc_warn')) - suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_enum_certificates'))) # AssertionError: OSError not raised by enum_certificates + suite.addTest(test.test_ssl.BasicSocketTests('test_enum_certificates')) suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_enum_crls'))) # AssertionError: [] is not true suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_errors'))) # AssertionError: OSError not raised suite.addTest(unittest.expectedFailure(test.test_ssl.BasicSocketTests('test_get_default_verify_paths'))) # AttributeError: 'module' object has no attribute 'get_default_verify_paths' @@ -55,8 +55,8 @@ def load_tests(loader, standard_tests, pattern): suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_default_certs_env_windows'))) # AttributeError: 'SSLContext' object has no attribute 'cert_store_stats' suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_dh_params'))) # AttributeError: 'SSLContext' object has no attribute 'load_dh_params' suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_verify_cadata'))) # AttributeError: 'SSLContext' object has no attribute 'cert_store_stats' - suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_verify_locations'))) # TypeError: expected str, got bytes - suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_options'))) # AssertionError: 2197818367 != 50331648 + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_verify_locations'))) # AssertionError: "PEM lib" does not match ... + suite.addTest(test.test_ssl.ContextTests('test_options')) suite.addTest(test.test_ssl.ContextTests('test_protocol')) suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_session_stats'))) # AttributeError: 'SSLContext' object has no attribute 'session_stats' suite.addTest(test.test_ssl.ContextTests('test_set_default_verify_paths')) @@ -75,17 +75,17 @@ def load_tests(loader, standard_tests, pattern): suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_connect_with_context'))) # AssertionError: {} != None suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_context_setget'))) # AttributeError: '_SSLSocket' object has no attribute 'context' suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_get_ca_certs_capath'))) # AttributeError: 'SSLContext' object has no attribute 'get_ca_certs' - suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_get_server_certificate'))) # TypeError: Value cannot be null. + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_get_server_certificate'))) # ssl.SSLError: [Errno 'errors while validating certificate chain: '] RemoteCertificateNameMismatch, RemoteCertificateChainErrors if is_posix: suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_makefile_close'))) # OSError: [Errno 9] Bad file descriptor else: suite.addTest(test.test_ssl.NetworkedTests('test_makefile_close')) suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_non_blocking_connect_ex'))) # OSError: [Errno -2146232800] The operation is not allowed on a non-blocking Socket. - suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_non_blocking_handshake'))) # TypeError: Value cannot be null. + suite.addTest(test.test_ssl.NetworkedTests('test_non_blocking_handshake')) suite.addTest(test.test_ssl.NetworkedTests('test_timeout_connect_ex')) suite.addTest(unittest.expectedFailure(test.test_ssl.SSLErrorTests('test_lib_reason'))) # AttributeError: 'SSLContext' object has no attribute 'load_dh_params' suite.addTest(unittest.expectedFailure(test.test_ssl.SSLErrorTests('test_str'))) # AssertionError: '[Errno 1] foo' != 'foo' - suite.addTest(unittest.expectedFailure(test.test_ssl.SSLErrorTests('test_subclass'))) # TypeError: Value cannot be null. + #suite.addTest(unittest.expectedFailure(test.test_ssl.SSLErrorTests('test_subclass'))) # blocking #suite.addTest(test.test_ssl.ThreadedTests('test_asyncore_server')) #suite.addTest(test.test_ssl.ThreadedTests('test_check_hostname')) #suite.addTest(test.test_ssl.ThreadedTests('test_compression')) From d9cb2711960d544f9b89ac05e92e8f4f62716da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Sat, 26 Mar 2022 15:42:37 -0400 Subject: [PATCH 4/5] Revert change --- Src/IronPython.Modules/_ssl.cs | 2 +- Tests/test_socket_stdlib.py | 2 +- Tests/test_ssl_stdlib.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Src/IronPython.Modules/_ssl.cs b/Src/IronPython.Modules/_ssl.cs index 63af45db8..7cc14f2c4 100644 --- a/Src/IronPython.Modules/_ssl.cs +++ b/Src/IronPython.Modules/_ssl.cs @@ -414,7 +414,7 @@ public void do_handshake() { if (_cert != null) { collection.Add(_cert); } - _sslStream.AuthenticateAsClient(_serverHostName ?? _socket._hostName ?? string.Empty, collection, enabledSslProtocols, false); + _sslStream.AuthenticateAsClient(_serverHostName ?? _socket._hostName, collection, enabledSslProtocols, false); } } catch (AuthenticationException e) { ((IDisposable)_socket._socket).Dispose(); diff --git a/Tests/test_socket_stdlib.py b/Tests/test_socket_stdlib.py index 7191abcac..663b098d3 100644 --- a/Tests/test_socket_stdlib.py +++ b/Tests/test_socket_stdlib.py @@ -519,7 +519,7 @@ def load_tests(loader, standard_tests, pattern): suite.addTest(unittest.expectedFailure(test.test_socket.UnbufferedFileObjectClassTestCase('testSmallReadNonBlocking'))) # TODO: figure out suite.addTest(test.test_socket.UnbufferedFileObjectClassTestCase('testUnbufferedRead')) suite.addTest(test.test_socket.UnbufferedFileObjectClassTestCase('testUnbufferedReadline')) - suite.addTest(test.test_socket.UnbufferedFileObjectClassTestCase('testWriteNonBlocking')) + #suite.addTest(test.test_socket.UnbufferedFileObjectClassTestCase('testWriteNonBlocking')) # fails intermittently during CI suite.addTest(test.test_socket.UnicodeReadFileObjectClassTestCase('testAttributes')) suite.addTest(test.test_socket.UnicodeReadFileObjectClassTestCase('testCloseAfterMakefile')) suite.addTest(test.test_socket.UnicodeReadFileObjectClassTestCase('testClosedAttr')) diff --git a/Tests/test_ssl_stdlib.py b/Tests/test_ssl_stdlib.py index 7ebb4cae9..7548b1c00 100644 --- a/Tests/test_ssl_stdlib.py +++ b/Tests/test_ssl_stdlib.py @@ -81,7 +81,7 @@ def load_tests(loader, standard_tests, pattern): else: suite.addTest(test.test_ssl.NetworkedTests('test_makefile_close')) suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_non_blocking_connect_ex'))) # OSError: [Errno -2146232800] The operation is not allowed on a non-blocking Socket. - suite.addTest(test.test_ssl.NetworkedTests('test_non_blocking_handshake')) + suite.addTest(unittest.expectedFailure(test.test_ssl.NetworkedTests('test_non_blocking_handshake'))) # TypeError: Value cannot be null. suite.addTest(test.test_ssl.NetworkedTests('test_timeout_connect_ex')) suite.addTest(unittest.expectedFailure(test.test_ssl.SSLErrorTests('test_lib_reason'))) # AttributeError: 'SSLContext' object has no attribute 'load_dh_params' suite.addTest(unittest.expectedFailure(test.test_ssl.SSLErrorTests('test_str'))) # AssertionError: '[Errno 1] foo' != 'foo' From 65db8fd3e208bd1007caf75eeddc895bf507d5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Lozier?= Date: Sat, 26 Mar 2022 18:41:51 -0400 Subject: [PATCH 5/5] Revert more changes --- Src/IronPython.Modules/_ssl.cs | 3 +-- Tests/test_ssl_stdlib.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Src/IronPython.Modules/_ssl.cs b/Src/IronPython.Modules/_ssl.cs index 7cc14f2c4..45a81be16 100644 --- a/Src/IronPython.Modules/_ssl.cs +++ b/Src/IronPython.Modules/_ssl.cs @@ -121,7 +121,6 @@ public _SSLContext(CodeContext context, int protocol) { this.protocol = protocol; - options = OP_ALL; if (protocol != PROTOCOL_SSLv2) options |= OP_NO_SSLv2; if (protocol != PROTOCOL_SSLv3) @@ -279,7 +278,7 @@ internal _SSLSocket(CodeContext context, _SSLContext sslcontext, PythonSocket.so EnsureSslStream(false); - _protocol = sslcontext.protocol; + _protocol = sslcontext.protocol | sslcontext.options; _validate = validate; _context = context; } diff --git a/Tests/test_ssl_stdlib.py b/Tests/test_ssl_stdlib.py index 7548b1c00..3fd588809 100644 --- a/Tests/test_ssl_stdlib.py +++ b/Tests/test_ssl_stdlib.py @@ -56,7 +56,7 @@ def load_tests(loader, standard_tests, pattern): suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_dh_params'))) # AttributeError: 'SSLContext' object has no attribute 'load_dh_params' suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_verify_cadata'))) # AttributeError: 'SSLContext' object has no attribute 'cert_store_stats' suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_load_verify_locations'))) # AssertionError: "PEM lib" does not match ... - suite.addTest(test.test_ssl.ContextTests('test_options')) + suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_options'))) # AssertionError: -2097150977 != 50331648 suite.addTest(test.test_ssl.ContextTests('test_protocol')) suite.addTest(unittest.expectedFailure(test.test_ssl.ContextTests('test_session_stats'))) # AttributeError: 'SSLContext' object has no attribute 'session_stats' suite.addTest(test.test_ssl.ContextTests('test_set_default_verify_paths'))