Skip to content

Commit 921c68e

Browse files
authored
Enable some ssl tests (#1376)
* Enable some ssl tests * Disable failing tests on Linux * Fix some tests and _SSLSocket cleanup * Revert change * Revert more changes
1 parent 130fda0 commit 921c68e

6 files changed

Lines changed: 205 additions & 63 deletions

File tree

Src/IronPython.Modules/_ssl.cs

Lines changed: 44 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,18 @@ public static void RAND_add(object buf, double entropy) {
110110

111111
[PythonType]
112112
public class _SSLContext {
113-
private readonly X509Certificate2Collection _cert_store = new X509Certificate2Collection();
114-
private string _cafile;
113+
internal readonly X509Certificate2Collection _cert_store = new X509Certificate2Collection();
114+
internal string _cafile;
115115
private int _verify_mode = SSL_VERIFY_NONE;
116116

117-
public _SSLContext(CodeContext context, int protocol = PROTOCOL_SSLv23) {
117+
public _SSLContext(CodeContext context, int protocol) {
118118
if (protocol != PROTOCOL_SSLv2 && protocol != PROTOCOL_SSLv23 && protocol != PROTOCOL_SSLv3 &&
119119
protocol != PROTOCOL_TLSv1 && protocol != PROTOCOL_TLSv1_1 && protocol != PROTOCOL_TLSv1_2) {
120120
throw PythonOps.ValueError("invalid protocol version");
121121
}
122122

123123
this.protocol = protocol;
124+
124125
if (protocol != PROTOCOL_SSLv2)
125126
options |= OP_NO_SSLv2;
126127
if (protocol != PROTOCOL_SSLv3)
@@ -176,14 +177,20 @@ public void load_cert_chain(string certfile, string keyfile = null, object passw
176177

177178
}
178179

179-
public void load_verify_locations(CodeContext context, string cafile = null, string capath = null, object cadata = null) {
180+
public void load_verify_locations(CodeContext context, object cafile = null, string capath = null, object cadata = null) {
180181
if (cafile == null && capath == null && cadata == null) {
181182
throw PythonOps.TypeError("cafile, capath and cadata cannot be all omitted");
182183
}
183184

184-
if (cafile != null) {
185-
_cert_store.Add(ReadCertificate(context, cafile));
186-
_cafile = cafile;
185+
if (cafile is not null) {
186+
if (cafile is string s) {
187+
_cafile = s;
188+
} else if (cafile is Bytes b) {
189+
_cafile = b.MakeString();
190+
} else {
191+
throw PythonOps.TypeError("cafile should be a valid filesystem path");
192+
}
193+
_cert_store.Add(ReadCertificate(context, _cafile));
187194
}
188195

189196
if (capath != null) {
@@ -208,8 +215,8 @@ public void load_verify_locations(CodeContext context, string cafile = null, str
208215
}
209216
}
210217

211-
public object _wrap_socket(CodeContext context, PythonSocket.socket sock, bool server_side, string server_hostname = null, object ssl_sock = null) {
212-
return new _SSLSocket(context, sock, server_side, null, _cafile, verify_mode, protocol | options, null, _cert_store) { _serverHostName = server_hostname };
218+
public object _wrap_socket(CodeContext context, PythonSocket.socket sock, bool server_side, string server_hostname = null) {
219+
return new _SSLSocket(context, this, sock, server_side, server_hostname);
213220
}
214221
}
215222

@@ -226,34 +233,22 @@ public class _SSLSocket {
226233
private Exception _validationFailure;
227234
internal string _serverHostName;
228235

229-
public _SSLSocket(CodeContext context, PythonSocket.socket sock, string keyfile = null, string certfile = null, X509Certificate2Collection certs = null) {
230-
_context = context;
231-
_sslStream = new SslStream(new NetworkStream(sock._socket, false), true, CertValidationCallback);
232-
_socket = sock;
233-
_protocol = PythonSsl.PROTOCOL_SSLv23 | PythonSsl.OP_NO_SSLv2 | PythonSsl.OP_NO_SSLv3;
234-
_validate = false;
235-
_certCollection = certs ?? new X509Certificate2Collection();
236-
}
236+
public _SSLContext context { get; }
237237

238-
internal _SSLSocket(CodeContext context,
239-
PythonSocket.socket sock,
240-
bool server_side,
241-
string keyfile = null,
242-
string certfile = null,
243-
int certs_mode = PythonSsl.CERT_NONE,
244-
int protocol = (PythonSsl.PROTOCOL_SSLv23 | PythonSsl.OP_NO_SSLv2 | PythonSsl.OP_NO_SSLv3),
245-
string cacertsfile = null,
246-
X509Certificate2Collection certs = null) {
238+
internal _SSLSocket(CodeContext context, _SSLContext sslcontext, PythonSocket.socket sock, bool server_side, string server_hostname) {
247239
if (sock == null) {
248240
throw PythonOps.TypeError("expected socket object, got None");
249241
}
250242

243+
this.context = sslcontext;
251244
_serverSide = server_side;
252-
bool validate;
253-
_certsMode = certs_mode;
245+
_serverHostName = server_hostname;
246+
247+
_certsMode = sslcontext.verify_mode;
254248

249+
bool validate;
255250
RemoteCertificateValidationCallback callback;
256-
switch (certs_mode) {
251+
switch (_certsMode) {
257252
case PythonSsl.CERT_NONE:
258253
validate = false;
259254
callback = CertValidationCallback;
@@ -267,28 +262,24 @@ internal _SSLSocket(CodeContext context,
267262
callback = CertValidationCallbackRequired;
268263
break;
269264
default:
270-
throw new InvalidOperationException(String.Format("bad certs_mode: {0}", certs_mode));
265+
throw new InvalidOperationException(String.Format("bad certs_mode: {0}", _certsMode));
271266
}
272267

273268
_callback = callback;
274269

275-
if (certs != null) {
276-
_certCollection = certs;
270+
if (sslcontext._cert_store != null) {
271+
_certCollection = sslcontext._cert_store;
277272
}
278273

279-
if (certfile != null) {
280-
_cert = PythonSsl.ReadCertificate(context, certfile);
281-
}
282-
283-
if (cacertsfile != null) {
284-
_certCollection = new X509Certificate2Collection(new[] { PythonSsl.ReadCertificate(context, cacertsfile) });
274+
if (sslcontext._cafile != null) {
275+
_cert = PythonSsl.ReadCertificate(context, sslcontext._cafile);
285276
}
286277

287278
_socket = sock;
288279

289280
EnsureSslStream(false);
290281

291-
_protocol = protocol;
282+
_protocol = sslcontext.protocol | sslcontext.options;
292283
_validate = validate;
293284
_context = context;
294285
}
@@ -522,7 +513,7 @@ public object peer_certificate(bool binary_form) {
522513

523514
if (peerCert != null) {
524515
if (binary_form) {
525-
return peerCert.GetRawCertData().MakeString();
516+
return Bytes.Make(peerCert.GetRawCertData());
526517
} else if (_validate) {
527518
return CertificateToPython(_context, peerCert);
528519
}
@@ -549,24 +540,23 @@ public string issuer() {
549540
return String.Empty;
550541
}
551542

552-
[Documentation(@"read([len]) -> string
553-
554-
Read up to len bytes from the SSL socket.")]
555-
public object read(CodeContext/*!*/ context, int len, ByteArray buffer = null) {
543+
[Documentation(@"read(size, [buffer])
544+
Read up to size bytes from the SSL socket.")]
545+
public object read(CodeContext/*!*/ context, int size, ByteArray buffer = null) {
556546
EnsureSslStream(true);
557547

558548
try {
559549
byte[] buf = new byte[2048];
560-
MemoryStream result = new MemoryStream(len);
550+
MemoryStream result = new MemoryStream(size);
561551
while (true) {
562-
int readLength = (len < buf.Length) ? len : buf.Length;
552+
int readLength = (size < buf.Length) ? size : buf.Length;
563553
int bytes = _sslStream.Read(buf, 0, readLength);
564554
if (bytes > 0) {
565555
result.Write(buf, 0, bytes);
566-
len -= bytes;
556+
size -= bytes;
567557
}
568558

569-
if (bytes == 0 || len == 0 || bytes < readLength) {
559+
if (bytes == 0 || size == 0 || bytes < readLength) {
570560
var res = result.ToArray();
571561
if (buffer == null)
572562
return Bytes.Make(res);
@@ -594,10 +584,9 @@ public string server() {
594584
return String.Empty;
595585
}
596586

597-
[Documentation(@"write(s) -> len
587+
[Documentation(@"Writes the bytes-like object b into the SSL object.
598588
599-
Writes the string s into the SSL object. Returns the number
600-
of bytes written.")]
589+
Returns the number of bytes written.")]
601590
public int write(CodeContext/*!*/ context, Bytes data) {
602591
EnsureSslStream(true);
603592

@@ -1124,8 +1113,10 @@ private static Exception ErrorDecoding(CodeContext context, params object[] args
11241113
public const int PROTOCOL_TLSv1_1 = 4;
11251114
public const int PROTOCOL_TLSv1_2 = 5;
11261115

1127-
public const uint OP_ALL = 0x80000BFF;
1128-
public const uint OP_DONT_INSERT_EMPTY_FRAGMENTS = 0x00000800;
1116+
public const int OP_ALL = unchecked((int)0x800003FF);
1117+
public const int OP_CIPHER_SERVER_PREFERENCE = 0x400000;
1118+
public const int OP_SINGLE_DH_USE = 0x100000;
1119+
public const int OP_SINGLE_ECDH_USE = 0x80000;
11291120
public const int OP_NO_SSLv2 = 0x01000000;
11301121
public const int OP_NO_SSLv3 = 0x02000000;
11311122
public const int OP_NO_TLSv1 = 0x04000000;

Src/IronPythonTest/Cases/CPythonCasesManifest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ RunCondition=$(IS_POSIX)
846846
[CPython.test_sqlite]
847847
Ignore=true
848848

849-
[CPython.test_ssl]
849+
[CPython.test_ssl] # IronPython.test_ssl_stdlib
850850
Ignore=true
851851
Reason=Blocking
852852

Src/StdLib/Lib/test/test_ssl.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def handle_error(prefix):
7676
sys.stdout.write(prefix + exc_format)
7777

7878
def can_clear_options():
79+
if sys.implementation.name == 'ironpython': return True
7980
# 0.9.8m or higher
8081
return ssl._OPENSSL_API_VERSION >= (0, 9, 8, 13, 15)
8182

@@ -569,7 +570,10 @@ def test_enum_certificates(self):
569570
self.assertTrue(ssl.enum_certificates("ROOT"))
570571

571572
self.assertRaises(TypeError, ssl.enum_certificates)
572-
self.assertRaises(WindowsError, ssl.enum_certificates, "")
573+
if sys.implementation.name == "ironpython":
574+
self.assertEqual(ssl.enum_certificates(""), [])
575+
else:
576+
self.assertRaises(WindowsError, ssl.enum_certificates, "")
573577

574578
trust_oids = set()
575579
for storename in ("CA", "ROOT"):

Tests/modules/network_related/test__ssl.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,22 @@ def test_constants(self):
2828
self.assertEqual(_ssl.CERT_NONE, 0)
2929
self.assertEqual(_ssl.CERT_OPTIONAL, 1)
3030
self.assertEqual(_ssl.CERT_REQUIRED, 2)
31-
self.assertEqual(_ssl.PROTOCOL_SSLv2, 0)
31+
if sys.version_info >= (3,5):
32+
self.assertRaises(AttributeError, lambda: _ssl.PROTOCOL_SSLv2)
33+
else:
34+
self.assertEqual(_ssl.PROTOCOL_SSLv2, 0)
3235
self.assertEqual(_ssl.PROTOCOL_SSLv23, 2)
33-
self.assertEqual(_ssl.PROTOCOL_SSLv3, 1)
36+
if sys.version_info >= (3,7):
37+
self.assertRaises(AttributeError, lambda: _ssl.PROTOCOL_SSLv3)
38+
else:
39+
self.assertEqual(_ssl.PROTOCOL_SSLv3, 1)
3440
self.assertEqual(_ssl.PROTOCOL_TLSv1, 3)
3541
self.assertEqual(_ssl.PROTOCOL_TLSv1_1, 4)
3642
self.assertEqual(_ssl.PROTOCOL_TLSv1_2, 5)
37-
self.assertEqual(_ssl.OP_NO_SSLv2, 0x1000000)
43+
if sys.version_info >= (3,7):
44+
self.assertEqual(_ssl.OP_NO_SSLv2, 0)
45+
else:
46+
self.assertEqual(_ssl.OP_NO_SSLv2, 0x1000000)
3847
self.assertEqual(_ssl.OP_NO_SSLv3, 0x2000000)
3948
self.assertEqual(_ssl.OP_NO_TLSv1, 0x4000000)
4049
self.assertEqual(_ssl.OP_NO_TLSv1_1, 0x10000000)
@@ -107,7 +116,8 @@ def test_SSLType_ssl(self):
107116
context = _ssl._SSLContext(_ssl.PROTOCOL_SSLv23)
108117
ssl_s = context._wrap_socket(s, False)
109118

110-
ssl_s.shutdown()
119+
if is_cli:
120+
ssl_s.shutdown()
111121
s.close()
112122

113123
#sock, keyfile, certfile
@@ -134,6 +144,7 @@ def test_SSLType_ssl_neg(self):
134144
#Cleanup
135145
s.close()
136146

147+
@skipUnlessIronPython()
137148
def test_SSLType_issuer(self):
138149
#--Positive
139150
s = socket.socket(socket.AF_INET)
@@ -166,6 +177,7 @@ def test_SSLType_issuer(self):
166177
ssl_s.shutdown()
167178
s.close()
168179

180+
@skipUnlessIronPython()
169181
def test_SSLType_server(self):
170182
#--Positive
171183
s = socket.socket(socket.AF_INET)
@@ -207,8 +219,12 @@ def test_SSLType_read_and_write(self):
207219
ssl_s = context._wrap_socket(s, False)
208220
ssl_s.do_handshake()
209221

210-
self.assertIn("Writes the string s into the SSL object.", ssl_s.write.__doc__)
211-
self.assertIn("Read up to len bytes from the SSL socket.", ssl_s.read.__doc__)
222+
if is_cli or sys.version_info >= (3,5):
223+
self.assertIn("Writes the bytes-like object b into the SSL object.", ssl_s.write.__doc__)
224+
self.assertIn("Read up to size bytes from the SSL socket.", ssl_s.read.__doc__)
225+
else:
226+
self.assertIn("Writes the string s into the SSL object.", ssl_s.write.__doc__)
227+
self.assertIn("Read up to len bytes from the SSL socket.", ssl_s.read.__doc__)
212228

213229
#Write
214230
self.assertEqual(ssl_s.write(SSL_REQUEST),
@@ -226,7 +242,8 @@ def test_SSLType_read_and_write(self):
226242
self.assertIn(SSL_RESPONSE, response)
227243

228244
#Cleanup
229-
ssl_s.shutdown()
245+
if is_cli:
246+
ssl_s.shutdown()
230247
s.close()
231248

232249
def test_parse_cert(self):

Tests/test_socket_stdlib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ def load_tests(loader, standard_tests, pattern):
519519
suite.addTest(unittest.expectedFailure(test.test_socket.UnbufferedFileObjectClassTestCase('testSmallReadNonBlocking'))) # TODO: figure out
520520
suite.addTest(test.test_socket.UnbufferedFileObjectClassTestCase('testUnbufferedRead'))
521521
suite.addTest(test.test_socket.UnbufferedFileObjectClassTestCase('testUnbufferedReadline'))
522-
suite.addTest(test.test_socket.UnbufferedFileObjectClassTestCase('testWriteNonBlocking'))
522+
#suite.addTest(test.test_socket.UnbufferedFileObjectClassTestCase('testWriteNonBlocking')) # fails intermittently during CI
523523
suite.addTest(test.test_socket.UnicodeReadFileObjectClassTestCase('testAttributes'))
524524
suite.addTest(test.test_socket.UnicodeReadFileObjectClassTestCase('testCloseAfterMakefile'))
525525
suite.addTest(test.test_socket.UnicodeReadFileObjectClassTestCase('testClosedAttr'))

0 commit comments

Comments
 (0)