Author: Richard Plangger <planri...@gmail.com> Branch: py3.5-ssl Changeset: r88427:742c85b008ab Date: 2016-11-17 10:05 +0100 http://bitbucket.org/pypy/pypy/changeset/742c85b008ab/
Log: copy over certificates of cpython stdlib (ssl + ssl_tests) target 3.5.2 diff --git a/lib-python/3/test/capath/0e4015b9.0 b/lib-python/3/test/capath/0e4015b9.0 new file mode 100644 --- /dev/null +++ b/lib-python/3/test/capath/0e4015b9.0 @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIIClTCCAf6gAwIBAgIJAKGU95wKR8pTMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV +BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u +IFNvZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAMMGnNlbGYtc2lnbmVkLnB5dGhv +bnRlc3QubmV0MB4XDTE0MTEwMjE4MDkyOVoXDTI0MTAzMDE4MDkyOVowcDELMAkG +A1UEBhMCWFkxFzAVBgNVBAcMDkNhc3RsZSBBbnRocmF4MSMwIQYDVQQKDBpQeXRo +b24gU29mdHdhcmUgRm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0 +aG9udGVzdC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANDXQXW9tjyZ +Xt0Iv2tLL1+jinr4wGg36ioLDLFkMf+2Y1GL0v0BnKYG4N1OKlAU15LXGeGer8vm +Sv/yIvmdrELvhAbbo3w4a9TMYQA4XkIVLdvu3mvNOAet+8PMJxn26dbDhG809ALv +EHY57lQsBS3G59RZyBPVqAqmImWNJnVzAgMBAAGjNzA1MCUGA1UdEQQeMByCGnNl +bGYtc2lnbmVkLnB5dGhvbnRlc3QubmV0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN +AQEFBQADgYEAIuzAhgMouJpNdf3URCHIineyoSt6WK/9+eyUcjlKOrDoXNZaD72h +TXMeKYoWvJyVcSLKL8ckPtDobgP2OTt0UkyAaj0n+ZHaqq1lH2yVfGUA1ILJv515 +C8BqbvVZuqm3i7ygmw3bqE/lYMgOrYtXXnqOrz6nvsE6Yc9V9rFflOM= +-----END CERTIFICATE----- diff --git a/lib-python/3/test/capath/ce7b8643.0 b/lib-python/3/test/capath/ce7b8643.0 new file mode 100644 --- /dev/null +++ b/lib-python/3/test/capath/ce7b8643.0 @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIIClTCCAf6gAwIBAgIJAKGU95wKR8pTMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV +BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u +IFNvZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAMMGnNlbGYtc2lnbmVkLnB5dGhv +bnRlc3QubmV0MB4XDTE0MTEwMjE4MDkyOVoXDTI0MTAzMDE4MDkyOVowcDELMAkG +A1UEBhMCWFkxFzAVBgNVBAcMDkNhc3RsZSBBbnRocmF4MSMwIQYDVQQKDBpQeXRo +b24gU29mdHdhcmUgRm91bmRhdGlvbjEjMCEGA1UEAwwac2VsZi1zaWduZWQucHl0 +aG9udGVzdC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANDXQXW9tjyZ +Xt0Iv2tLL1+jinr4wGg36ioLDLFkMf+2Y1GL0v0BnKYG4N1OKlAU15LXGeGer8vm +Sv/yIvmdrELvhAbbo3w4a9TMYQA4XkIVLdvu3mvNOAet+8PMJxn26dbDhG809ALv +EHY57lQsBS3G59RZyBPVqAqmImWNJnVzAgMBAAGjNzA1MCUGA1UdEQQeMByCGnNl +bGYtc2lnbmVkLnB5dGhvbnRlc3QubmV0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN +AQEFBQADgYEAIuzAhgMouJpNdf3URCHIineyoSt6WK/9+eyUcjlKOrDoXNZaD72h +TXMeKYoWvJyVcSLKL8ckPtDobgP2OTt0UkyAaj0n+ZHaqq1lH2yVfGUA1ILJv515 +C8BqbvVZuqm3i7ygmw3bqE/lYMgOrYtXXnqOrz6nvsE6Yc9V9rFflOM= +-----END CERTIFICATE----- diff --git a/lib-python/3/test/selfsigned_pythontestdotnet.pem b/lib-python/3/test/selfsigned_pythontestdotnet.pem --- a/lib-python/3/test/selfsigned_pythontestdotnet.pem +++ b/lib-python/3/test/selfsigned_pythontestdotnet.pem @@ -1,5 +1,5 @@ -----BEGIN CERTIFICATE----- -MIIChzCCAfCgAwIBAgIJAKGU95wKR8pSMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV +MIIClTCCAf6gAwIBAgIJAKGU95wKR8pTMA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV BAYTAlhZMRcwFQYDVQQHDA5DYXN0bGUgQW50aHJheDEjMCEGA1UECgwaUHl0aG9u IFNvZnR3YXJlIEZvdW5kYXRpb24xIzAhBgNVBAMMGnNlbGYtc2lnbmVkLnB5dGhv bnRlc3QubmV0MB4XDTE0MTEwMjE4MDkyOVoXDTI0MTAzMDE4MDkyOVowcDELMAkG @@ -8,9 +8,9 @@ aG9udGVzdC5uZXQwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANDXQXW9tjyZ Xt0Iv2tLL1+jinr4wGg36ioLDLFkMf+2Y1GL0v0BnKYG4N1OKlAU15LXGeGer8vm Sv/yIvmdrELvhAbbo3w4a9TMYQA4XkIVLdvu3mvNOAet+8PMJxn26dbDhG809ALv -EHY57lQsBS3G59RZyBPVqAqmImWNJnVzAgMBAAGjKTAnMCUGA1UdEQQeMByCGnNl -bGYtc2lnbmVkLnB5dGhvbnRlc3QubmV0MA0GCSqGSIb3DQEBBQUAA4GBAIOXmdtM -eG9qzP9TiXW/Gc/zI4cBfdCpC+Y4gOfC9bQUC7hefix4iO3+iZjgy3X/FaRxUUoV -HKiXcXIaWqTSUWp45cSh0MbwZXudp6JIAptzdAhvvCrPKeC9i9GvxsPD4LtDAL97 -vSaxQBezA7hdxZd90/EeyMgVZgAnTCnvAWX9 +EHY57lQsBS3G59RZyBPVqAqmImWNJnVzAgMBAAGjNzA1MCUGA1UdEQQeMByCGnNl +bGYtc2lnbmVkLnB5dGhvbnRlc3QubmV0MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN +AQEFBQADgYEAIuzAhgMouJpNdf3URCHIineyoSt6WK/9+eyUcjlKOrDoXNZaD72h +TXMeKYoWvJyVcSLKL8ckPtDobgP2OTt0UkyAaj0n+ZHaqq1lH2yVfGUA1ILJv515 +C8BqbvVZuqm3i7ygmw3bqE/lYMgOrYtXXnqOrz6nvsE6Yc9V9rFflOM= -----END CERTIFICATE----- diff --git a/lib-python/3/test/test_ssl.py b/lib-python/3/test/test_ssl.py --- a/lib-python/3/test/test_ssl.py +++ b/lib-python/3/test/test_ssl.py @@ -23,9 +23,6 @@ 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): return os.path.join(os.path.dirname(__file__), *name) @@ -57,8 +54,6 @@ 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") @@ -148,8 +143,8 @@ def test_str_for_enums(self): # Make sure that the PROTOCOL_* constants have enum-like string # reprs. - proto = ssl.PROTOCOL_TLS - self.assertEqual(str(proto), '_SSLMethod.PROTOCOL_TLS') + proto = ssl.PROTOCOL_SSLv23 + self.assertEqual(str(proto), '_SSLMethod.PROTOCOL_SSLv23') ctx = ssl.SSLContext(proto) self.assertIs(ctx.protocol, proto) @@ -281,27 +276,6 @@ 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', '<unsupported>'), - ('othername', '<unsupported>'), - ('email', 'u...@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() @@ -338,8 +312,8 @@ self.assertGreaterEqual(status, 0) self.assertLessEqual(status, 15) # Version string as returned by {Open,Libre}SSL, the format might change - if IS_LIBRESSL: - self.assertTrue(s.startswith("LibreSSL {:d}".format(major)), + if "LibreSSL" in s: + self.assertTrue(s.startswith("LibreSSL {:d}.{:d}".format(major, minor)), (s, t, hex(n))) else: self.assertTrue(s.startswith("OpenSSL {:d}.{:d}.{:d}".format(major, minor, fix)), @@ -816,8 +790,7 @@ def test_constructor(self): for protocol in PROTOCOLS: ssl.SSLContext(protocol) - ctx = ssl.SSLContext() - self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLS) + self.assertRaises(TypeError, ssl.SSLContext) self.assertRaises(ValueError, ssl.SSLContext, -1) self.assertRaises(ValueError, ssl.SSLContext, 42) @@ -838,15 +811,15 @@ def test_options(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) # OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value - default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3) - if not IS_LIBRESSL and ssl.OPENSSL_VERSION_INFO >= (1, 1, 0): - default |= ssl.OP_NO_COMPRESSION - self.assertEqual(default, ctx.options) + self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3, + ctx.options) ctx.options |= ssl.OP_NO_TLSv1 - self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options) + 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_TLSv1) - self.assertEqual(default, ctx.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 # Ubuntu has OP_NO_SSLv3 forced on by default self.assertEqual(0, ctx.options & ~ssl.OP_NO_SSLv3) @@ -1182,7 +1155,6 @@ 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: @@ -1778,13 +1750,13 @@ sslobj = ctx.wrap_bio(incoming, outgoing, False, REMOTE_HOST) self.assertIs(sslobj._sslobj.owner, sslobj) self.assertIsNone(sslobj.cipher()) - self.assertIsNotNone(sslobj.shared_ciphers()) + self.assertIsNone(sslobj.shared_ciphers()) self.assertRaises(ValueError, sslobj.getpeercert) if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES: self.assertIsNone(sslobj.get_channel_binding('tls-unique')) self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake) self.assertTrue(sslobj.cipher()) - self.assertIsNotNone(sslobj.shared_ciphers()) + self.assertIsNone(sslobj.shared_ciphers()) self.assertTrue(sslobj.getpeercert()) if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES: self.assertTrue(sslobj.get_channel_binding('tls-unique')) @@ -1898,14 +1870,7 @@ else: self.sock.close() - # PyPy change def run(self): - try: - self._run() - finally: - self.close() - - def _run(self): self.running = True if not self.server.starttls_server: if not self.wrap_conn(): @@ -2668,7 +2633,7 @@ s.close() def test_socketserver(self): - """Using socketserver to create and manage SSL connections.""" + """Using a SocketServer to create and manage SSL connections.""" server = make_https_server(self, certfile=CERTFILE) # try to connect if support.verbose: @@ -2695,6 +2660,8 @@ def test_asyncore_server(self): """Check the example asyncore integration.""" + indata = "TEST MESSAGE of mixed case\n" + if support.verbose: sys.stdout.write("\n") @@ -2826,13 +2793,20 @@ # consume data s.read() + data = b"data" + # 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) + # recv/read(0) should return no data + s.send(data) + self.assertEqual(s.recv(0), b"") + self.assertEqual(s.read(0), b"") + self.assertEqual(s.read(), data) + # Make sure sendmsg et al are disallowed to avoid # inadvertent disclosure of data and/or corruption # of the encrypted data stream @@ -2848,26 +2822,6 @@ 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_nonblocking_send(self): server = ThreadedEchoServer(CERTFILE, certreqs=ssl.CERT_NONE, @@ -2889,10 +2843,8 @@ # will be full and the call will block buf = bytearray(8192) def fill_buffer(): - i = 0 while True: s.send(buf) - i += 1 self.assertRaises((ssl.SSLWantWriteError, ssl.SSLWantReadError), fill_buffer) @@ -3028,7 +2980,7 @@ with 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_ECDH, "test requires ECDH-enabled OpenSSL") @@ -3170,36 +3122,24 @@ (['http/3.0', 'http/4.0'], None) ] for client_protocols, expected in protocol_tests: - server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) server_context.load_cert_chain(CERTFILE) server_context.set_alpn_protocols(server_protocols) - client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) client_context.load_cert_chain(CERTFILE) client_context.set_alpn_protocols(client_protocols) - - 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: - # OpenSSL 1.1.0 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")) + 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")) def test_selected_npn_protocol(self): # selected_npn_protocol() is None unless NPN is used @@ -3347,23 +3287,13 @@ client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) client_context.verify_mode = ssl.CERT_REQUIRED client_context.load_verify_locations(SIGNING_CA) - if ssl.OPENSSL_VERSION_INFO >= (1, 0, 2): - client_context.set_ciphers("AES128:AES256") - server_context.set_ciphers("AES256") - alg1 = "AES256" - alg2 = "AES-256" - else: - client_context.set_ciphers("AES:3DES") - server_context.set_ciphers("3DES") - alg1 = "3DES" - alg2 = "DES-CBC3" - + client_context.set_ciphers("RC4") + server_context.set_ciphers("AES:RC4") stats = server_params_test(client_context, server_context) ciphers = stats['server_shared_ciphers'][0] self.assertGreater(len(ciphers), 0) for name, tls_version, bits in ciphers: - if not alg1 in name.split("-") and alg2 not in name: - self.fail(name) + self.assertIn("RC4", name.split("-")) def test_read_write_after_close_raises_valuerror(self): context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) diff --git a/lib_pypy/openssl/_cffi_src/openssl/err.py b/lib_pypy/openssl/_cffi_src/openssl/err.py --- a/lib_pypy/openssl/_cffi_src/openssl/err.py +++ b/lib_pypy/openssl/_cffi_src/openssl/err.py @@ -196,9 +196,34 @@ static const int SSL_TLSEXT_ERR_ALERT_FATAL; static const int SSL_TLSEXT_ERR_NOACK; +static const int SSL_AD_CLOSE_NOTIFY; +static const int SSL_AD_UNEXPECTED_MESSAGE; +static const int SSL_AD_BAD_RECORD_MAC; +static const int SSL_AD_RECORD_OVERFLOW; +static const int SSL_AD_DECOMPRESSION_FAILURE; +static const int SSL_AD_HANDSHAKE_FAILURE; +static const int SSL_AD_BAD_CERTIFICATE; +static const int SSL_AD_UNSUPPORTED_CERTIFICATE; +static const int SSL_AD_CERTIFICATE_REVOKED; +static const int SSL_AD_CERTIFICATE_EXPIRED; +static const int SSL_AD_CERTIFICATE_UNKNOWN; +static const int SSL_AD_ILLEGAL_PARAMETER; +static const int SSL_AD_UNKNOWN_CA; +static const int SSL_AD_ACCESS_DENIED; +static const int SSL_AD_DECODE_ERROR; +static const int SSL_AD_DECRYPT_ERROR; +static const int SSL_AD_PROTOCOL_VERSION; +static const int SSL_AD_INSUFFICIENT_SECURITY; static const int SSL_AD_INTERNAL_ERROR; -static const int SSL_AD_ACCESS_DENIED; -static const int SSL_AD_HANDSHAKE_FAILURE; +static const int SSL_AD_USER_CANCELLED; +static const int SSL_AD_NO_RENEGOTIATION; + +static const int SSL_AD_UNSUPPORTED_EXTENSION; +static const int SSL_AD_CERTIFICATE_UNOBTAINABLE; +static const int SSL_AD_UNRECOGNIZED_NAME; +static const int SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE; +static const int SSL_AD_BAD_CERTIFICATE_HASH_VALUE; +static const int SSL_AD_UNKNOWN_PSK_IDENTITY; """ FUNCTIONS = """ @@ -255,4 +280,24 @@ static const long Cryptography_HAS_RSA_R_PKCS_DECODING_ERROR = 0; static const long RSA_R_PKCS_DECODING_ERROR = 0; #endif + +#ifndef SSL_AD_UNSUPPORTED_EXTENSION + static const int SSL_AD_UNSUPPORTED_EXTENSION = -1; +#endif +#ifndef SSL_AD_CERTIFICATE_UNOBTAINABLE + static const int SSL_AD_CERTIFICATE_UNOBTAINABLE = -1; +#endif +#ifndef SSL_AD_UNRECOGNIZED_NAME + static const int SSL_AD_UNRECOGNIZED_NAME = -1; +#endif +#ifndef SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE + static const int SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE = -1; +#endif +#ifndef SSL_AD_BAD_CERTIFICATE_HASH_VALUE + static const int SSL_AD_BAD_CERTIFICATE_HASH_VALUE = -1; +#endif +#ifndef SSL_AD_UNKNOWN_PSK_IDENTITY + static const int SSL_AD_UNKNOWN_PSK_IDENTITY = -1; +#endif + """ diff --git a/lib_pypy/openssl/_cffi_src/openssl/ssl.py b/lib_pypy/openssl/_cffi_src/openssl/ssl.py --- a/lib_pypy/openssl/_cffi_src/openssl/ssl.py +++ b/lib_pypy/openssl/_cffi_src/openssl/ssl.py @@ -695,9 +695,9 @@ static const long Cryptography_HAS_SSL_CTX_CLEAR_OPTIONS = 1; #ifdef OPENSSL_NO_TLSEXT +static const long Cryptography_NO_TLSEXT = 1; +#else static const long Cryptography_NO_TLSEXT = 0; -#else -static const long Cryptography_NO_TLSEXT = 1; #endif #ifdef OPENSSL_NPN_NEGOTIATED diff --git a/lib_pypy/openssl/_stdssl/__init__.py b/lib_pypy/openssl/_stdssl/__init__.py --- a/lib_pypy/openssl/_stdssl/__init__.py +++ b/lib_pypy/openssl/_stdssl/__init__.py @@ -8,7 +8,7 @@ from openssl._stdssl.certificate import (_test_decode_cert, _decode_certificate, _certificate_to_der) from openssl._stdssl.utility import (_str_with_len, _bytes_with_len, - _str_to_ffi_buffer, _str_from_buf) + _str_to_ffi_buffer, _str_from_buf, _cstr_decode_fs) from openssl._stdssl.error import (ssl_error, pyssl_error, SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, SSLSyscallError, @@ -17,8 +17,9 @@ SSL_ERROR_SSL, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_X509_LOOKUP, SSL_ERROR_SYSCALL, SSL_ERROR_ZERO_RETURN, SSL_ERROR_WANT_CONNECT, - SSL_ERROR_EOF, SSL_ERROR_NO_SOCKET, SSL_ERROR_INVALID_ERROR_CODE) - + SSL_ERROR_EOF, SSL_ERROR_NO_SOCKET, SSL_ERROR_INVALID_ERROR_CODE, + pyerr_write_unraisable) +from openssl._stdssl import error OPENSSL_VERSION = ffi.string(lib.OPENSSL_VERSION_TEXT).decode('utf-8') OPENSSL_VERSION_NUMBER = lib.OPENSSL_VERSION_NUMBER @@ -85,6 +86,12 @@ else: CHANNEL_BINDING_TYPES = [] +for name in error.SSL_AD_NAMES: + lib_attr = 'SSL_AD_' + name + attr = 'ALERT_DESCRIPTION_' + name + if hasattr(lib, lib_attr): + globals()[attr] = getattr(lib, lib_attr) + # init open ssl lib.SSL_load_error_strings() lib.SSL_library_init() @@ -103,6 +110,7 @@ callable = None password = None operationerror = None + handle = None PWINFO_STORAGE = {} def _Cryptography_pem_password_cb(buf, size, rwflag, userdata): @@ -258,8 +266,8 @@ self._owner = None self.server_hostname = None self.socket = None - self.alpn_protocols = ffi.NULL - self.npn_protocols = ffi.NULL + #self.alpn_protocols = ffi.NULL + #self.npn_protocols = ffi.NULL @property def owner(self): @@ -710,7 +718,7 @@ class _SSLContext(object): __slots__ = ('ctx', '_check_hostname', 'servername_callback', 'alpn_protocols', 'npn_protocols', 'set_hostname', - '_set_hostname_handle') + '_set_hostname_handle', '_npn_protocols_handle') def __new__(cls, protocol): self = object.__new__(cls) @@ -861,8 +869,6 @@ pw_info = PasswordInfo() index = -1 if password is not None: - index = _thread.get_ident() - PWINFO_STORAGE[index] = pw_info if callable(password): pw_info.callable = password @@ -872,9 +878,11 @@ else: raise TypeError("password should be a string or callable") - handle = ffi.new_handle(pw_info) # XXX MUST NOT be garbage collected + pw_info.handle = ffi.new_handle(pw_info) + index = _thread.get_ident() + PWINFO_STORAGE[index] = pw_info lib.SSL_CTX_set_default_passwd_cb(self.ctx, Cryptography_pem_password_cb) - lib.SSL_CTX_set_default_passwd_cb_userdata(self.ctx, handle) + lib.SSL_CTX_set_default_passwd_cb_userdata(self.ctx, pw_info.handle) try: ffi.errno = 0 @@ -908,7 +916,7 @@ ret = lib.SSL_CTX_check_private_key(self.ctx) if ret != 1: - raise _ssl_seterror(None, -1) + raise ssl_error(None) finally: if index >= 0: del PWINFO_STORAGE[index] @@ -1033,6 +1041,7 @@ return {'x509': x509, 'x509_ca': x509_ca, 'crl': crl} +# REVIEW, how to do that properly # def _finalize_(self): # ctx = self.ctx # if ctx: @@ -1125,8 +1134,8 @@ return if not callable(callback): raise TypeError("not a callable object") - self.set_hostname = callback - self._set_hostname_handle = ffi.new_handle(self) + scb = ServernameCallback(callback, self) + self._set_hostname_handle = ffi.new_handle(scb) lib.Cryptography_SSL_CTX_set_tlsext_servername_callback(self.ctx, _servername_callback) lib.Cryptography_SSL_CTX_set_tlsext_servername_arg(self.ctx, self._set_hostname_handle) @@ -1146,6 +1155,7 @@ if HAS_NPN: self.npn_protocols = ffi.from_buffer(protos) handle = ffi.new_handle(self) + self._npn_protocols_handle = handle # track a reference to the handle lib.SSL_CTX_set_next_protos_advertised_cb(self.ctx, advertise_npn_callback, handle) lib.SSL_CTX_set_next_proto_select_cb(self.ctx, select_npn_callback, handle) else: @@ -1156,20 +1166,21 @@ if HAS_SNI and not lib.Cryptography_NO_TLSEXT: @ffi.callback("int(SSL*,int*,void*)") def _servername_callback(s, al, arg): - ssl_ctx = ffi.from_handle(arg) + scb = ffi.from_handle(arg) + ssl_ctx = scb.ctx servername = lib.SSL_get_servername(s, lib.TLSEXT_NAMETYPE_host_name) - #ifdef WITH_THREAD - # TODO PyGILState_STATE gstate = PyGILState_Ensure(); - #endif + set_hostname = scb.callback + #ifdef WITH_THREAD + # TODO PyGILState_STATE gstate = PyGILState_Ensure(); + #endif - if ssl_ctx.set_hostname is None: + if set_hostname is None: #/* remove race condition in this the call back while if removing the # * callback is in progress */ - #ifdef WITH_THREAD - # TODO PyGILState_Release(gstate); - #endif + #ifdef WITH_THREAD + # TODO PyGILState_Release(gstate); + #endif return lib.SSL_TLSEXT_ERR_OK - #} ssl = ffi.from_handle(lib.SSL_get_app_data(s)) assert isinstance(ssl, _SSLSocket) @@ -1192,10 +1203,9 @@ if servername == ffi.NULL: try: - result = ssl_ctx.set_hostname(ssl_socket, None, ssl_ctx) - except: - # TODO - # PyErr_WriteUnraisable(ssl_ctx->set_hostname); + result = set_hostname(ssl_socket, None, ssl_ctx) + except Exception as e: + pyerr_write_unraisable(e, set_hostname) al[0] = lib.SSL_AD_HANDSHAKE_FAILURE return lib.SSL_TLSEXT_ERR_ALERT_FATAL else: @@ -1203,31 +1213,44 @@ try: servername_idna = servername.decode("idna") - except UnicodeDecodeError: - raise # TODO? - # PyErr_WriteUnraisable(servername_o); + except UnicodeDecodeError as e: + pyerr_write_unraisable(e, servername) try: - result = ssl_ctx.set_hostname(ssl_socket, servername_idna, ssl_ctx) - except: - # TODO - # PyErr_WriteUnraisable(ssl_ctx->set_hostname); + result = set_hostname(ssl_socket, servername_idna, ssl_ctx) + except Exception as e: + pyerr_write_unraisable(e, set_hostname) al[0] = lib.SSL_AD_HANDSHAKE_FAILURE return lib.SSL_TLSEXT_ERR_ALERT_FATAL if result is not None: + # this is just a poor man's emulation: + # in CPython this works a bit different, it calls all the way + # down from PyLong_AsLong to _PyLong_FromNbInt which raises + # a TypeError if there is no nb_int slot filled. try: - al[0] = int(result) - except: - # PyErr_WriteUnraisable(result); - al[0] = lib.SSL_AD_INTERNAL_ERROR - return lib.SSL_TLSEXT_ERR_ALERT_FATAL + if isinstance(result, int): + al[0] = result + else: + if result is not None: + if hasattr(result,'__int__'): + al[0] = result.__int__() + return lib.SSL_TLSEXT_ERR_ALERT_FATAL + # needed because sys.exec_info is used in pyerr_write_unraisable + raise TypeError("an integer is required (got type %s)" % result) + except TypeError as e: + pyerr_write_unraisable(e, result) + al[0] = lib.SSL_AD_INTERNAL_ERROR + return lib.SSL_TLSEXT_ERR_ALERT_FATAL else: # TODO gil state release? return lib.SSL_TLSEXT_ERR_OK class ServernameCallback(object): - ctx = None + def __init__(self, callback, ctx): + self.callback = callback + self.ctx = ctx + SERVERNAME_CALLBACKS = weakref.WeakValueDictionary() TEST = None @@ -1287,13 +1310,13 @@ """Whether the memory BIO is at EOF.""" return lib.BIO_ctrl_pending(self.bio) == 0 and self.eof_written - def write(self, _bytes): + def write(self, strlike): INT_MAX = 2**31-1 - if isinstance(_bytes, memoryview): - # REVIEW pypy does not support from_buffer of a memoryview - # copies the data! - _bytes = bytes(_bytes) - buf = ffi.from_buffer(_bytes) + if isinstance(strlike, memoryview): + # FIXME pypy must support get_raw_address for + # StringBuffer to remove this case! + strlike = strlike.tobytes() + buf = ffi.from_buffer(strlike) if len(buf) > INT_MAX: raise OverflowError("string longer than %d bytes", INT_MAX) @@ -1346,7 +1369,7 @@ ok = lib.RAND_bytes(buf, count) if ok == 1: return ffi.string(buf) - raise ssl_error("", errcode=lib.ERR_get_error()) + raise ssl_error(None, errcode=lib.ERR_get_error()) def RAND_pseudo_bytes(count): return _RAND_bytes(count, True) @@ -1358,19 +1381,6 @@ buf = _str_to_ffi_buffer(view) lib.RAND_add(buf, len(buf), entropy) - -def _cstr_decode_fs(buf): -#define CONVERT(info, target) { \ -# const char *tmp = (info); \ -# target = NULL; \ -# if (!tmp) { Py_INCREF(Py_None); target = Py_None; } \ -# else if ((target = PyUnicode_DecodeFSDefault(tmp)) == NULL) { \ -# target = PyBytes_FromString(tmp); } \ -# if (!target) goto error; \ -# } - # REVIEW - return ffi.string(buf).decode(sys.getfilesystemencoding()) - def get_default_verify_paths(): ofile_env = _cstr_decode_fs(lib.X509_get_default_cert_file_env()) @@ -1385,7 +1395,6 @@ odir = _cstr_decode_fs(lib.X509_get_default_cert_dir()) if odir is None: return odir - return (ofile_env, ofile, odir_env, odir); @ffi.callback("int(SSL*,unsigned char **,unsigned char *,const unsigned char *,unsigned int,void *)") @@ -1395,28 +1404,28 @@ ffi.cast("unsigned char*",ctx.alpn_protocols), len(ctx.alpn_protocols), client_protocols, client_protocols_len) -@ffi.callback("int(SSL*,unsigned char **,unsigned char *,const unsigned char *,unsigned int,void *)") -def select_npn_callback(ssl, out, outlen, server_protocols, server_protocols_len, args): - ctx = ffi.from_handle(args) - return do_protocol_selection(0, out, outlen, server_protocols, server_protocols_len, - ffi.cast("unsigned char*",ctx.npn_protocols), len(ctx.npn_protocols)) +if lib.Cryptography_HAS_NPN_NEGOTIATED: + @ffi.callback("int(SSL*,unsigned char **,unsigned char *,const unsigned char *,unsigned int,void *)") + def select_npn_callback(ssl, out, outlen, server_protocols, server_protocols_len, args): + ctx = ffi.from_handle(args) + return do_protocol_selection(0, out, outlen, server_protocols, server_protocols_len, + ffi.cast("unsigned char*",ctx.npn_protocols), len(ctx.npn_protocols)) -@ffi.callback("int(SSL*,const unsigned char**, unsigned int*, void*)") -def advertise_npn_callback(ssl, data, length, args): - ctx = ffi.from_handle(args) + @ffi.callback("int(SSL*,const unsigned char**, unsigned int*, void*)") + def advertise_npn_callback(ssl, data, length, args): + ctx = ffi.from_handle(args) - if not ctx.npn_protocols: - data[0] = ffi.new("unsigned char*", b"") - length[0] = 0 - else: - data[0] = ffi.cast("unsigned char*",ctx.npn_protocols) - length[0] = len(ctx.npn_protocols) + if not ctx.npn_protocols: + data[0] = ffi.new("unsigned char*", b"") + length[0] = 0 + else: + data[0] = ffi.cast("unsigned char*",ctx.npn_protocols) + length[0] = len(ctx.npn_protocols) - return lib.SSL_TLSEXT_ERR_OK + return lib.SSL_TLSEXT_ERR_OK -if lib.Cryptography_HAS_NPN_NEGOTIATED: def do_protocol_selection(alpn, out, outlen, server_protocols, server_protocols_len, client_protocols, client_protocols_len): if client_protocols == ffi.NULL: diff --git a/lib_pypy/openssl/_stdssl/error.py b/lib_pypy/openssl/_stdssl/error.py --- a/lib_pypy/openssl/_stdssl/error.py +++ b/lib_pypy/openssl/_stdssl/error.py @@ -1,3 +1,5 @@ +import sys +import traceback from _openssl import ffi from _openssl import lib @@ -46,7 +48,7 @@ if errstr is None: errcode = lib.ERR_peek_last_error() try: - return fill_sslerror(SSLError, errcode, errstr) + return fill_sslerror(SSLError, errcode, errstr, errcode) finally: lib.ERR_clear_error() @@ -136,6 +138,7 @@ lib_str = LIB_CODES_TO_NAMES.get(err_lib, None) if errstr is None: errstr = _str_from_buf(lib.ERR_reason_error_string(errcode)) + msg = errstr if not errstr: msg = "unknown error" if reason_str and lib_str: @@ -148,3 +151,49 @@ err_value.library = lib_str if lib_str else None return err_value +def pyerr_write_unraisable(exc, obj): + f = sys.stderr + + if obj: + f.write("Exception ignored in: ") + f.write(repr(obj)) + f.write("\n") + + t, v, tb = sys.exc_info() + traceback.print_tb(tb, file=f) + + assert isinstance(v, Exception) + f.write(t.__module__ + "." + t.__name__) + f.write(": ") + f.write(str(v)) + f.write("\n") + +SSL_AD_NAMES = [ + "ACCESS_DENIED", + "BAD_CERTIFICATE", + "BAD_CERTIFICATE_HASH_VALUE", + "BAD_CERTIFICATE_STATUS_RESPONSE", + "BAD_RECORD_MAC", + "CERTIFICATE_EXPIRED", + "CERTIFICATE_REVOKED", + "CERTIFICATE_UNKNOWN", + "CERTIFICATE_UNOBTAINABLE", + "CLOSE_NOTIFY", + "DECODE_ERROR", + "DECOMPRESSION_FAILURE", + "DECRYPT_ERROR", + "HANDSHAKE_FAILURE", + "ILLEGAL_PARAMETER", + "INSUFFICIENT_SECURITY", + "INTERNAL_ERROR", + "NO_RENEGOTIATION", + "PROTOCOL_VERSION", + "RECORD_OVERFLOW", + "UNEXPECTED_MESSAGE", + "UNKNOWN_CA", + "UNKNOWN_PSK_IDENTITY", + "UNRECOGNIZED_NAME", + "UNSUPPORTED_CERTIFICATE", + "UNSUPPORTED_EXTENSION", + "USER_CANCELLED", +] diff --git a/lib_pypy/openssl/_stdssl/utility.py b/lib_pypy/openssl/_stdssl/utility.py --- a/lib_pypy/openssl/_stdssl/utility.py +++ b/lib_pypy/openssl/_stdssl/utility.py @@ -1,3 +1,4 @@ +import sys from _openssl import ffi from _openssl import lib @@ -36,3 +37,15 @@ def _str_from_buf(buf): return ffi.string(buf).decode('utf-8') +def _cstr_decode_fs(buf): +#define CONVERT(info, target) { \ +# const char *tmp = (info); \ +# target = NULL; \ +# if (!tmp) { Py_INCREF(Py_None); target = Py_None; } \ +# else if ((target = PyUnicode_DecodeFSDefault(tmp)) == NULL) { \ +# target = PyBytes_FromString(tmp); } \ +# if (!target) goto error; \ +# } + # REVIEW + return ffi.string(buf).decode(sys.getfilesystemencoding()) + _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit