diff -r 5ad5efc847c7 Doc/library/ssl.rst --- a/Doc/library/ssl.rst Mon Dec 10 00:03:55 2012 +0200 +++ b/Doc/library/ssl.rst Mon Dec 10 14:26:43 2012 +1100 @@ -533,6 +533,42 @@ .. versionadded:: 3.2 +.. data:: ALERT_DESCRIPTION_CLOSE_NOTIFY + ALERT_DESCRIPTION_UNEXPECTED_MESSAGE + ALERT_DESCRIPTION_BAD_RECORD_MAC + ALERT_DESCRIPTION_RECORD_OVERFLOW + ALERT_DESCRIPTION_DECOMPRESSION_FAILURE + ALERT_DESCRIPTION_HANDSHAKE_FAILURE + ALERT_DESCRIPTION_BAD_CERTIFICATE + ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE + ALERT_DESCRIPTION_CERTIFICATE_REVOKED + ALERT_DESCRIPTION_CERTIFICATE_EXPIRED + ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN + ALERT_DESCRIPTION_ILLEGAL_PARAMETER + ALERT_DESCRIPTION_UNKNOWN_CA + ALERT_DESCRIPTION_ACCESS_DENIED + ALERT_DESCRIPTION_DECODE_ERROR + ALERT_DESCRIPTION_DECRYPT_ERROR + ALERT_DESCRIPTION_PROTOCOL_VERSION + ALERT_DESCRIPTION_INSUFFICIENT_SECURITY + ALERT_DESCRIPTION_INTERNAL_ERROR + ALERT_DESCRIPTION_USER_CANCELLED + ALERT_DESCRIPTION_NO_RENEGOTIATION + ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION + ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE + ALERT_DESCRIPTION_UNRECOGNIZED_NAME + ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE + ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE + ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY + + Alert Descriptions from :rfc:`5246` and others. The `IANA TLS Alert Registry + `_ + contains this list and references to the RFCs where their meaning is defined. + + Used as the return value of the callback function in + :meth:`SSLContext.set_servername_callback`. + + .. versionadded:: 3.4 SSL Sockets ----------- @@ -780,6 +816,49 @@ .. versionadded:: 3.3 +.. method:: SSLContext.set_servername_callback(server_name_callback) + + Register a callback function that will be called after the TLS Client Hello + handshake message has been received by the SSL/TLS server when a client + specifies a server name indicatation. The server name indication mechanism + is specified in :rfc:`6066` section 3 - Extension Definations. + + Only one callback can be set per ``SSLContext``. If *server_name_callback* + is ``None`` then the callback is disabled. + + The callback function, *server_name_callback*, will be called with three + arguments the first being the :class:`_ssl._SSLSocket` (a simplified + :class:`ssl.SSLSocket`), the second is a string that represents the server + name that the client is intending to communicate and the third argument is + the original :class:`SSLContext`. The server name argument is the IDNA + decoded server name. + + A typical use of this callback is to change the :class:`_ssl._SSLSocket`'s + ``context`` attribute to a new object of type :class:`SSLContext` + representing a certificate chain that matches the server name. Other methods + of use on the :class:`_ssl._SSLSocket` are + :meth:`SSLSocket.selected_npn_protocol` and :meth:`SSLSocket.cipher`. + + The *server_name_callback* function must return ``None`` if there are no + the TLS negiotation will be allowed continue. If the function requires a + failure a constant + :const:`ALERT_DESCRIPTION_* ` can be + returned. Other return values will result in a TLS fatal error + :const:`ALERT_DESCRIPTION_INTERNAL_ERROR`. + + If there is a IDNA decoding error on the server name, the TLS connection + will terminate with an :const:`ALERT_DESCRIPTION_INTERNAL_ERROR` fatal TLS + alert message to the client. + + If an exception is raised from the *server_name_callback* function the TLS + connection will terminate with a fatal TLS alert message + :const:`ALERT_DESCRIPTION_HANDSHAKE_FAILURE`. + + This method will raise :exc:`NotImplementedError` if the OpenSSL library has + OPENSSL_NO_TLSEXT defined when it was built. + + .. versionadded:: 3.4 + .. method:: SSLContext.load_dh_params(dhfile) Load the key generation parameters for Diffie-Helman (DH) key exchange. @@ -1313,3 +1392,12 @@ `RFC 4366: Transport Layer Security (TLS) Extensions `_ Blake-Wilson et. al. + + `RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 `_ + T. Dierks et. al. + + `RFC 6066: Transport Layer Security (TLS) Extensions `_ + D. Eastlake + + `IANA TLS: Transport Layer Security (TLS) Parameters `_ + IANA diff -r 5ad5efc847c7 Lib/ssl.py --- a/Lib/ssl.py Mon Dec 10 00:03:55 2012 +0200 +++ b/Lib/ssl.py Mon Dec 10 14:26:43 2012 +1100 @@ -52,6 +52,37 @@ PROTOCOL_SSLv3 PROTOCOL_SSLv23 PROTOCOL_TLSv1 + +The following constants identify various SSL alert message descriptions as per +http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6 + +ALERT_DESCRIPTION_CLOSE_NOTIFY +ALERT_DESCRIPTION_UNEXPECTED_MESSAGE +ALERT_DESCRIPTION_BAD_RECORD_MAC +ALERT_DESCRIPTION_RECORD_OVERFLOW +ALERT_DESCRIPTION_DECOMPRESSION_FAILURE +ALERT_DESCRIPTION_HANDSHAKE_FAILURE +ALERT_DESCRIPTION_BAD_CERTIFICATE +ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE +ALERT_DESCRIPTION_CERTIFICATE_REVOKED +ALERT_DESCRIPTION_CERTIFICATE_EXPIRED +ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN +ALERT_DESCRIPTION_ILLEGAL_PARAMETER +ALERT_DESCRIPTION_UNKNOWN_CA +ALERT_DESCRIPTION_ACCESS_DENIED +ALERT_DESCRIPTION_DECODE_ERROR +ALERT_DESCRIPTION_DECRYPT_ERROR +ALERT_DESCRIPTION_PROTOCOL_VERSION +ALERT_DESCRIPTION_INSUFFICIENT_SECURITY +ALERT_DESCRIPTION_INTERNAL_ERROR +ALERT_DESCRIPTION_USER_CANCELLED +ALERT_DESCRIPTION_NO_RENEGOTIATION +ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION +ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE +ALERT_DESCRIPTION_UNRECOGNIZED_NAME +ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE +ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE +ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY """ import textwrap @@ -91,6 +122,37 @@ SSL_ERROR_INVALID_ERROR_CODE, ) from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN + +from _ssl import ( + ALERT_DESCRIPTION_CLOSE_NOTIFY, + ALERT_DESCRIPTION_UNEXPECTED_MESSAGE, + ALERT_DESCRIPTION_BAD_RECORD_MAC, + ALERT_DESCRIPTION_RECORD_OVERFLOW, + ALERT_DESCRIPTION_DECOMPRESSION_FAILURE, + ALERT_DESCRIPTION_HANDSHAKE_FAILURE, + ALERT_DESCRIPTION_BAD_CERTIFICATE, + ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE, + ALERT_DESCRIPTION_CERTIFICATE_REVOKED, + ALERT_DESCRIPTION_CERTIFICATE_EXPIRED, + ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN, + ALERT_DESCRIPTION_ILLEGAL_PARAMETER, + ALERT_DESCRIPTION_UNKNOWN_CA, + ALERT_DESCRIPTION_ACCESS_DENIED, + ALERT_DESCRIPTION_DECODE_ERROR, + ALERT_DESCRIPTION_DECRYPT_ERROR, + ALERT_DESCRIPTION_PROTOCOL_VERSION, + ALERT_DESCRIPTION_INSUFFICIENT_SECURITY, + ALERT_DESCRIPTION_INTERNAL_ERROR, + ALERT_DESCRIPTION_USER_CANCELLED, + ALERT_DESCRIPTION_NO_RENEGOTIATION, + ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION, + ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE, + ALERT_DESCRIPTION_UNRECOGNIZED_NAME, + ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE, + ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE, + ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY, +) + from _ssl import (PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1) from _ssl import _OPENSSL_API_VERSION @@ -236,7 +298,7 @@ _context=None): if _context: - self.context = _context + self._context = _context else: if server_side and not certfile: raise ValueError("certfile must be specified for server-side " @@ -245,16 +307,16 @@ raise ValueError("certfile must be specified") if certfile and not keyfile: keyfile = certfile - self.context = SSLContext(ssl_version) - self.context.verify_mode = cert_reqs + self._context = SSLContext(ssl_version) + self._context.verify_mode = cert_reqs if ca_certs: - self.context.load_verify_locations(ca_certs) + self._context.load_verify_locations(ca_certs) if certfile: - self.context.load_cert_chain(certfile, keyfile) + self._context.load_cert_chain(certfile, keyfile) if npn_protocols: - self.context.set_npn_protocols(npn_protocols) + self._context.set_npn_protocols(npn_protocols) if ciphers: - self.context.set_ciphers(ciphers) + self._context.set_ciphers(ciphers) self.keyfile = keyfile self.certfile = certfile self.cert_reqs = cert_reqs @@ -296,7 +358,7 @@ if connected: # create the SSL object try: - self._sslobj = self.context._wrap_socket(self, server_side, + self._sslobj = self._context._wrap_socket(self, server_side, server_hostname) if do_handshake_on_connect: timeout = self.gettimeout() @@ -308,6 +370,14 @@ except socket_error as x: self.close() raise x + @property + def context(self): + return self._context + + @context.setter + def context(self, ctx): + self._context = ctx + self._sslobj.context = ctx def dup(self): raise NotImplemented("Can't dup() %s instances" % diff -r 5ad5efc847c7 Lib/test/keycert3.pem --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/keycert3.pem Mon Dec 10 14:26:43 2012 +1100 @@ -0,0 +1,74 @@ +-----BEGIN PRIVATE KEY----- +MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALE7BeiL9d5HT4k1 +lwOmGUPONdCkUZA5qRXv7fhmXyPDL2m3CezPF1fx5tUtJNU1r83zsv+BhSfNfcXW +hyio1vob1DZhVIMr2S4qT3pCrsh+n9IykzBRcBR02hwckanPELvLSKy3xK8w1mOH +2YGCL1VOl3l4o5jRlm8gLhiz98izAgMBAAECgYBurk2iTMXfaulg5vbIcJKWpdE1 +22iUiW3vJUeYOVZxHxQdERJBsZMCeS+PMgIupLiGBgtjRZ/Cm7JQOwltewPFY/qe +gXffjuhaOOK9daZ9vyDafccmlzYcH4VbldtJOmW+tVkEIubd4lph6SMsc3Y3LSex +lBel3IoKXThlyKpCAQJBAN/oUmoZlHdNrJnyRnByRRk/RyZLmjtGGMk46VpLR65u +EFMTiGx86OzcihccL4JAjRLJTaSIaM4NZKr5BCAvf6sCQQDKogRqYVslyLJk8vrT +AyWwC1EC3KzqJnWhaZXdwOJ+A8nsjNnchmsi/6CS7zlwZ2jwJz+rne1vhTHX3f5a +b/MZAkB2tNukpoUgrwx33YtcqChcF0EoW2KcVDBl1gdZOJn7Lxz2fl+QbLSshsur +LuU9H+3q7NB+D1fJDi61uilmyufbAkAkYdi3HQnXi+Rz+aiNcJg4tp9yCxtE42jl +dWbCLRq9k4KS5WtMjBn6a7lyCF/gzcBqtjFZZ/60sk6TurkrlcQpAkAGj151++Ol +CgVN1lcNRM+Z/guW2Eyd6HdJXpLdjjMsgqijlgl9uImDfbkkQ60hEagweU4/MbKB +IyIvCAeEKhE0 +-----END PRIVATE KEY----- +Certificate: + Data: + Version: 1 (0x0) + Serial Number: + fa:3a:c2:6a:81:bc:43:01 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=XY, O=Python Software Foundation CA, CN=the_ca_server_of.python.org + Validity + Not Before: Dec 5 02:47:17 2012 GMT + Not After : Oct 14 02:47:17 2022 GMT + Subject: C=XY, L=Castle Anthrax, O=Python Software Foundation, CN=localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:b1:3b:05:e8:8b:f5:de:47:4f:89:35:97:03:a6: + 19:43:ce:35:d0:a4:51:90:39:a9:15:ef:ed:f8:66: + 5f:23:c3:2f:69:b7:09:ec:cf:17:57:f1:e6:d5:2d: + 24:d5:35:af:cd:f3:b2:ff:81:85:27:cd:7d:c5:d6: + 87:28:a8:d6:fa:1b:d4:36:61:54:83:2b:d9:2e:2a: + 4f:7a:42:ae:c8:7e:9f:d2:32:93:30:51:70:14:74: + da:1c:1c:91:a9:cf:10:bb:cb:48:ac:b7:c4:af:30: + d6:63:87:d9:81:82:2f:55:4e:97:79:78:a3:98:d1: + 96:6f:20:2e:18:b3:f7:c8:b3 + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 5d:c3:10:b8:6a:f6:7b:0a:8d:ca:bc:30:fd:0d:5d:8b:fe:33: + 37:e4:92:3e:e9:56:7e:5e:e7:28:33:d5:cb:a7:59:1d:db:80: + 1e:33:1e:ff:e7:19:b5:30:a0:bf:9f:c2:7a:3a:6f:0f:a5:bd: + c7:8e:58:8f:44:b5:df:1b:b0:5a:2c:d0:ca:ff:8f:c8:93:d1: + d7:7f:39:a8:e9:2a:1c:72:01:7d:52:12:e2:83:26:ce:c7:1d: + 87:54:f1:66:e7:6d:c0:d7:25:b8:89:57:60:3e:b7:a7:df:6e: + 6d:c1:ee:9a:5a:ab:bb:66:44:87:94:28:3b:03:1c:bc:69:e4: + f7:bf:d4:85:a6:58:1d:f7:7a:3a:82:09:14:2b:2b:3b:aa:c5: + 38:f8:2c:c2:a6:92:5c:a7:d6:b8:f6:22:b4:3c:b7:ae:8b:ed: + cd:ce:df:e4:a0:dc:c0:65:92:82:0c:a7:47:c8:8f:33:38:c7: + 31:6c:4f:59:8d:c5:83:12:14:7c:e7:f5:02:bb:ad:52:6b:66: + e9:e8:00:f2:bb:b8:d9:ba:bd:8b:3d:e2:c6:47:11:9d:c1:a0: + ce:b3:d8:37:eb:e9:39:d7:0b:79:d8:bf:ec:d2:2f:0e:09:3f: + 3c:84:a8:68:d5:c4:44:16:7a:12:b5:bb:b1:34:d5:fd:d7:7c: + 9f:f6:c9:6e +-----BEGIN CERTIFICATE----- +MIICsjCCAZoCCQD6OsJqgbxDATANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQGEwJY +WTEmMCQGA1UECgwdUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24gQ0ExJDAiBgNV +BAMMG3RoZV9jYV9zZXJ2ZXJfb2YucHl0aG9uLm9yZzAeFw0xMjEyMDUwMjQ3MTda +Fw0yMjEwMTQwMjQ3MTdaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUg +QW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQ +BgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsTsF +6Iv13kdPiTWXA6YZQ8410KRRkDmpFe/t+GZfI8MvabcJ7M8XV/Hm1S0k1TWvzfOy +/4GFJ819xdaHKKjW+hvUNmFUgyvZLipPekKuyH6f0jKTMFFwFHTaHByRqc8Qu8tI +rLfErzDWY4fZgYIvVU6XeXijmNGWbyAuGLP3yLMCAwEAATANBgkqhkiG9w0BAQUF +AAOCAQEAXcMQuGr2ewqNyrww/Q1di/4zN+SSPulWfl7nKDPVy6dZHduAHjMe/+cZ +tTCgv5/CejpvD6W9x45Yj0S13xuwWizQyv+PyJPR1385qOkqHHIBfVIS4oMmzscd +h1TxZudtwNcluIlXYD63p99ubcHumlqru2ZEh5QoOwMcvGnk97/UhaZYHfd6OoIJ +FCsrO6rFOPgswqaSXKfWuPYitDy3rovtzc7f5KDcwGWSggynR8iPMzjHMWxPWY3F +gxIUfOf1ArutUmtm6egA8ru42bq9iz3ixkcRncGgzrPYN+vpOdcLedi/7NIvDgk/ +PISoaNXERBZ6ErW7sTTV/dd8n/bJbg== +-----END CERTIFICATE----- diff -r 5ad5efc847c7 Lib/test/keycert4.pem --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/keycert4.pem Mon Dec 10 14:26:43 2012 +1100 @@ -0,0 +1,74 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAL89FfPs6FzeHCZ/ +8EhQi7j3jds95RWtFmfQGpAHUvBjo/K1WFnbdYEROH/BAfAaZzgIw51yVVDUY7B9 +0/9BrOG9tNAPghsCPOWKqLTV8XHHl3M6yESxRSdkcRvQmpt8RkkENDdJi+yrpw9g +QlgD3co8f4Ymzfzfx47AxIdw49CDAgMBAAECgYAIne8ES7N4o8Vu8qrYB1EUZ4sr +ytEstdzhlp3ryXiyPjsf743olpvn0efS0daNhBMMklCGIqW3e6p2RGUuBj6M5Xe8 +zixNOBGfpKen00vBE/Suqz3RJJS6I4GkMSUZJuov9BoT8lBV56L/a7Tqv0LBvpKy +hwnHwCEqjULveqz8MQJBAN5eEM+bHrYHHzqbCyX+lVYSviN7w+4RdILtH4NpwM4g +ZGJ2E3/wBFru6IQsjG/NzC74e5O35AOPaSIlEFV1JxsCQQDcKbsCbcG/smAUTAoB +eaJMb2nvo4IHRBuVgZWXM0F+yPih6dP/VVJXZy1M7Z4i+Rr6rVmo/hoU7DuaKM6c +0oq5AkBNo4Y3Fi/42i+7JJsV3veBH/YnKXVVT97gP657EZdx4Llr71ILn1/F6Bhi +3bMC7lXWKIftIY2I+K96bnpvfAH1AkAuBR8NE+oy6tNAiuBwjH9LYOKQ7+mnISpu +5uZg85rWKKYoGhtxPOF4WZ3TfbvXbaChE4kxVOQYyYRv5eXCTBQpAkEAlKNu1d8W +pItZXeXmKXwuOiVLm0Bjm3tCkYOM3iexyCflUDZhXS7GX7el7SKN5YrylvZOZphv +PEQYmpXI8YBGZQ== +-----END PRIVATE KEY----- +Certificate: + Data: + Version: 1 (0x0) + Serial Number: + fa:3a:c2:6a:81:bc:43:02 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=XY, O=Python Software Foundation CA, CN=the_ca_server_of.python.org + Validity + Not Before: Dec 5 02:47:17 2012 GMT + Not After : Oct 14 02:47:17 2022 GMT + Subject: C=XY, L=Castle Anthrax, O=Python Software Foundation, CN=fakehostname + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:bf:3d:15:f3:ec:e8:5c:de:1c:26:7f:f0:48:50: + 8b:b8:f7:8d:db:3d:e5:15:ad:16:67:d0:1a:90:07: + 52:f0:63:a3:f2:b5:58:59:db:75:81:11:38:7f:c1: + 01:f0:1a:67:38:08:c3:9d:72:55:50:d4:63:b0:7d: + d3:ff:41:ac:e1:bd:b4:d0:0f:82:1b:02:3c:e5:8a: + a8:b4:d5:f1:71:c7:97:73:3a:c8:44:b1:45:27:64: + 71:1b:d0:9a:9b:7c:46:49:04:34:37:49:8b:ec:ab: + a7:0f:60:42:58:03:dd:ca:3c:7f:86:26:cd:fc:df: + c7:8e:c0:c4:87:70:e3:d0:83 + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 31:a0:4e:95:02:9d:7e:48:a5:9f:e7:ab:18:6b:f0:62:05:6b: + ce:e7:a8:16:bd:19:09:12:cf:3e:30:28:21:82:71:a5:a5:45: + 19:c1:d6:24:56:77:d7:e6:d9:77:88:3f:57:23:06:71:be:be: + ff:0f:7c:78:8e:70:45:8a:b3:86:cb:89:a5:36:45:a2:c2:42: + b6:7d:4c:d6:2f:d4:5d:2d:f8:57:55:59:8a:d6:a9:19:f0:68: + a4:5e:f3:78:21:51:64:ef:f6:12:ae:3d:ba:96:1f:5e:d5:27: + 44:64:b9:c2:93:56:ca:f1:58:43:f8:ba:98:19:03:32:7a:77: + 88:14:69:70:1d:fe:0d:e0:1c:f5:7d:ce:ca:6d:28:5d:cd:c9: + 99:ae:4f:33:c4:05:27:31:b0:33:d5:38:b2:51:2c:79:be:2b: + cb:9d:1a:45:cd:5d:34:72:e0:cb:cc:5e:1f:b3:1f:84:a6:ab: + b5:c7:12:8a:a2:7a:77:e4:98:df:81:df:f6:5a:58:ab:47:ff: + b3:8d:5e:76:8f:b5:9a:02:42:b5:73:2e:b7:0b:a2:4f:2a:5d: + 3d:89:30:3c:fa:1e:88:37:e5:72:1d:a0:e3:78:12:3e:e8:25: + 5b:de:29:bd:a6:b6:d3:df:5c:b2:c0:39:40:f6:c3:0b:e2:0d: + 8b:9e:5e:12 +-----BEGIN CERTIFICATE----- +MIICtTCCAZ0CCQD6OsJqgbxDAjANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQGEwJY +WTEmMCQGA1UECgwdUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24gQ0ExJDAiBgNV +BAMMG3RoZV9jYV9zZXJ2ZXJfb2YucHl0aG9uLm9yZzAeFw0xMjEyMDUwMjQ3MTda +Fw0yMjEwMTQwMjQ3MTdaMGIxCzAJBgNVBAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUg +QW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24xFTAT +BgNVBAMTDGZha2Vob3N0bmFtZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA +vz0V8+zoXN4cJn/wSFCLuPeN2z3lFa0WZ9AakAdS8GOj8rVYWdt1gRE4f8EB8Bpn +OAjDnXJVUNRjsH3T/0Gs4b200A+CGwI85YqotNXxcceXczrIRLFFJ2RxG9Cam3xG +SQQ0N0mL7KunD2BCWAPdyjx/hibN/N/HjsDEh3Dj0IMCAwEAATANBgkqhkiG9w0B +AQUFAAOCAQEAMaBOlQKdfkiln+erGGvwYgVrzueoFr0ZCRLPPjAoIYJxpaVFGcHW +JFZ31+bZd4g/VyMGcb6+/w98eI5wRYqzhsuJpTZFosJCtn1M1i/UXS34V1VZitap +GfBopF7zeCFRZO/2Eq49upYfXtUnRGS5wpNWyvFYQ/i6mBkDMnp3iBRpcB3+DeAc +9X3Oym0oXc3Jma5PM8QFJzGwM9U4slEseb4ry50aRc1dNHLgy8xeH7MfhKartccS +iqJ6d+SY34Hf9lpYq0f/s41edo+1mgJCtXMutwuiTypdPYkwPPoeiDflch2g43gS +PuglW94pvaa2099cssA5QPbDC+INi55eEg== +-----END CERTIFICATE----- diff -r 5ad5efc847c7 Lib/test/make_ssl_certs.py --- a/Lib/test/make_ssl_certs.py Mon Dec 10 00:03:55 2012 +0200 +++ b/Lib/test/make_ssl_certs.py Mon Dec 10 14:26:43 2012 +1100 @@ -20,11 +20,52 @@ [req_x509_extensions] subjectAltName = DNS:{hostname} - """ + +[ ca ] +default_ca = CA_default + +[ CA_default ] +dir = cadir +database = $dir/index.txt +default_md = sha1 +default_days = 3600 +certificate = pycacert.pem +private_key = pycakey.pem +serial = $dir/serial +RANDFILE = $dir/.rand + +policy = policy_match + +[ policy_match ] +countryName = match +stateOrProvinceName = optional +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + + +[ v3_ca ] + +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid:always,issuer +basicConstraints = CA:true + +""" here = os.path.abspath(os.path.dirname(__file__)) -def make_cert_key(hostname): +def make_cert_key(hostname, sign=False): + print("creating cert for " + hostname) tempnames = [] for i in range(3): with tempfile.NamedTemporaryFile(delete=False) as f: @@ -33,10 +74,25 @@ try: with open(req_file, 'w') as f: f.write(req_template.format(hostname=hostname)) - args = ['req', '-new', '-days', '3650', '-nodes', '-x509', + args = ['req', '-new', '-days', '3650', '-nodes', '-newkey', 'rsa:1024', '-keyout', key_file, - '-out', cert_file, '-config', req_file] + '-config', req_file] + if sign: + with tempfile.NamedTemporaryFile(delete=False) as f: + tempnames.append(f.name) + reqfile = f.name + args += ['-out', reqfile ] + + else: + args += ['-x509', '-out', cert_file ] check_call(['openssl'] + args) + + if sign: + args = ['ca', '-config', req_file, '-out', cert_file, '-outdir', 'cadir', + '-policy', 'policy_anything', '-batch', '-infiles', reqfile ] + check_call(['openssl'] + args) + + with open(cert_file, 'r') as f: cert = f.read() with open(key_file, 'r') as f: @@ -47,6 +103,29 @@ os.remove(name) +def make_ca(): + try: + os.mkdir('cadir') + except OSError: + pass + with open(os.path.join('cadir','index.txt'),'a+') as f: + pass # empty file + with open(os.path.join('cadir','index.txt.attr'),'w+') as f: + f.write('unique_subject = no') + + with tempfile.NamedTemporaryFile() as t: + t.write(req_template) + t.flush() + with tempfile.NamedTemporaryFile() as f: + args = ['req', '-new', '-days', '3650', '-extensions', 'v3_ca', '-nodes', + '-newkey', 'rsa:2048', '-keyout', 'pycakey.pem', + '-out', f.name, '-subj', '/C=XY/L=Castle Anthrax/O=Python Software Foundation CA/CN=the_ca_server_of.python.org'] + check_call(['openssl'] + args) + args = ['ca', '-config', t.name, '-create_serial', '-out', 'pycacert.pem', '-batch', '-outdir', 'cadir', + '-keyfile', 'pycakey.pem', '-days', '3650', + '-selfsign', '-extensions', 'v3_ca', '-infiles', f.name ] + check_call(['openssl'] + args) + if __name__ == '__main__': os.chdir(here) cert, key = make_cert_key('localhost') @@ -54,11 +133,30 @@ f.write(cert) with open('ssl_key.pem', 'w') as f: f.write(key) + print("password protecting ssl_key.pem in ssl_key.passwd.pem") + check_call(['openssl','rsa','-in','ssl_key.pem','-out','ssl_key.passwd.pem','-des3','-passout','pass:somepass']) + check_call(['openssl','rsa','-in','ssl_key.pem','-out','keycert.passwd.pem','-des3','-passout','pass:somepass']) + with open('keycert.pem', 'w') as f: f.write(key) f.write(cert) + + with open('keycert.passwd.pem', 'a+') as f: + f.write(cert) + # For certificate matching tests + make_ca() cert, key = make_cert_key('fakehostname') with open('keycert2.pem', 'w') as f: f.write(key) f.write(cert) + + cert, key = make_cert_key('localhost',True) + with open('keycert3.pem', 'w') as f: + f.write(key) + f.write(cert) + + cert, key = make_cert_key('fakehostname',True) + with open('keycert4.pem', 'w') as f: + f.write(key) + f.write(cert) diff -r 5ad5efc847c7 Lib/test/pycacert.pem --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/pycacert.pem Mon Dec 10 14:26:43 2012 +1100 @@ -0,0 +1,79 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + fa:3a:c2:6a:81:bc:43:00 + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=XY, O=Python Software Foundation CA, CN=the_ca_server_of.python.org + Validity + Not Before: Dec 5 02:47:17 2012 GMT + Not After : Dec 3 02:47:17 2022 GMT + Subject: C=XY, O=Python Software Foundation CA, CN=the_ca_server_of.python.org + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a0:77:38:a6:b7:46:29:0f:cb:b2:9e:c1:70:fd: + 96:2e:8b:d8:d7:47:3d:40:be:d0:63:72:73:91:fe: + 06:b3:cc:7d:bc:08:c4:cb:8b:c2:47:6f:17:cd:8b: + c5:6f:cc:41:a2:2a:46:f1:4f:65:fb:43:e7:34:c1: + b9:52:8a:63:dd:a1:4d:1a:4e:2a:80:32:e6:b6:e2: + 25:83:f4:63:cc:5d:37:b1:51:9a:cf:d3:35:e1:aa: + e1:e3:aa:e3:d9:b3:77:f1:e4:39:55:d0:ee:34:65: + c0:66:2c:2f:f4:06:d9:b6:93:bd:00:12:63:b5:1b: + be:cc:57:fb:9d:05:24:5c:e3:6b:dd:8e:c5:82:a1: + 35:16:a0:21:cf:2f:c3:45:da:99:76:42:27:6b:c3: + a0:ed:40:91:5c:11:7a:e8:b3:79:2b:2d:94:97:ed: + 32:91:ea:a3:f9:97:ca:22:34:13:01:44:7c:79:7f: + b5:49:bf:c4:4c:2c:ac:64:76:ae:25:e9:10:ab:6e: + 09:15:f5:1b:ea:bb:bd:29:e6:f7:3c:7f:6f:d7:7f: + 79:72:3b:a2:73:c6:37:4f:85:cf:58:1a:3a:77:6e: + a2:c0:14:bd:13:63:b8:02:a1:4d:26:57:4d:a9:a5: + 66:fb:4d:24:98:30:b5:46:29:80:da:3f:c5:ed:d5: + a3:55 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + 7A:E5:62:B1:09:44:7F:25:EF:FD:03:EF:2B:71:F3:DA:D0:54:17:1C + X509v3 Authority Key Identifier: + keyid:7A:E5:62:B1:09:44:7F:25:EF:FD:03:EF:2B:71:F3:DA:D0:54:17:1C + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 47:15:a9:8e:2d:e9:fc:b4:27:05:63:4f:ae:62:f5:a9:31:07: + c2:a2:f3:ae:7a:01:26:cd:ed:6f:95:24:9f:0c:3e:00:eb:68: + bd:ff:b4:35:11:20:86:c5:d2:bf:b0:92:88:96:21:3a:72:e8: + b1:a4:c5:80:5d:41:0c:1e:fc:dd:dc:77:3d:9b:45:fe:d5:c5: + f2:cd:65:03:82:ec:77:1c:09:5a:91:5e:a6:da:ac:79:2b:35: + a0:17:29:65:56:f1:17:dc:10:63:e2:93:78:33:d1:be:b0:3e: + c4:fe:8c:5f:71:0a:fa:7e:8f:5f:a7:bd:76:ca:73:f0:66:ed: + 57:91:a3:30:1e:b1:07:09:e7:b6:6e:11:69:be:15:2f:48:9f: + b5:e7:7e:dc:85:cc:df:c1:b9:c4:55:78:45:9b:8e:53:5c:05: + 79:f6:3b:48:ff:bf:e3:18:8b:b8:7e:7f:6b:a2:e4:fd:10:dc: + 4a:a0:fe:04:0b:5b:b3:72:52:e6:3a:3d:eb:96:ed:2a:57:5a: + c6:23:04:f1:8e:c4:6d:82:99:60:c9:4d:95:a4:54:8c:fc:f9: + 7d:19:7c:54:5d:92:06:0e:c1:7e:7b:87:ee:83:e8:ea:9d:c5: + f9:41:72:ee:9d:41:c5:ad:05:91:01:69:b0:be:3d:c1:1e:23: + 5d:b1:1e:53 +-----BEGIN CERTIFICATE----- +MIIDiTCCAnGgAwIBAgIJAPo6wmqBvEMAMA0GCSqGSIb3DQEBBQUAMFsxCzAJBgNV +BAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUgRm91bmRhdGlvbiBDQTEk +MCIGA1UEAwwbdGhlX2NhX3NlcnZlcl9vZi5weXRob24ub3JnMB4XDTEyMTIwNTAy +NDcxN1oXDTIyMTIwMzAyNDcxN1owWzELMAkGA1UEBhMCWFkxJjAkBgNVBAoMHVB5 +dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uIENBMSQwIgYDVQQDDBt0aGVfY2Ffc2Vy +dmVyX29mLnB5dGhvbi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB +AQCgdzimt0YpD8uynsFw/ZYui9jXRz1AvtBjcnOR/gazzH28CMTLi8JHbxfNi8Vv +zEGiKkbxT2X7Q+c0wblSimPdoU0aTiqAMua24iWD9GPMXTexUZrP0zXhquHjquPZ +s3fx5DlV0O40ZcBmLC/0Btm2k70AEmO1G77MV/udBSRc42vdjsWCoTUWoCHPL8NF +2pl2Qidrw6DtQJFcEXros3krLZSX7TKR6qP5l8oiNBMBRHx5f7VJv8RMLKxkdq4l +6RCrbgkV9Rvqu70p5vc8f2/Xf3lyO6JzxjdPhc9YGjp3bqLAFL0TY7gCoU0mV02p +pWb7TSSYMLVGKYDaP8Xt1aNVAgMBAAGjUDBOMB0GA1UdDgQWBBR65WKxCUR/Je/9 +A+8rcfPa0FQXHDAfBgNVHSMEGDAWgBR65WKxCUR/Je/9A+8rcfPa0FQXHDAMBgNV +HRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBHFamOLen8tCcFY0+uYvWpMQfC +ovOuegEmze1vlSSfDD4A62i9/7Q1ESCGxdK/sJKIliE6cuixpMWAXUEMHvzd3Hc9 +m0X+1cXyzWUDgux3HAlakV6m2qx5KzWgFyllVvEX3BBj4pN4M9G+sD7E/oxfcQr6 +fo9fp712ynPwZu1XkaMwHrEHCee2bhFpvhUvSJ+1537chczfwbnEVXhFm45TXAV5 +9jtI/7/jGIu4fn9rouT9ENxKoP4EC1uzclLmOj3rlu0qV1rGIwTxjsRtgplgyU2V +pFSM/Pl9GXxUXZIGDsF+e4fug+jqncX5QXLunUHFrQWRAWmwvj3BHiNdsR5T +-----END CERTIFICATE----- diff -r 5ad5efc847c7 Lib/test/pycakey.pem --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/pycakey.pem Mon Dec 10 14:26:43 2012 +1100 @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgdzimt0YpD8uy +nsFw/ZYui9jXRz1AvtBjcnOR/gazzH28CMTLi8JHbxfNi8VvzEGiKkbxT2X7Q+c0 +wblSimPdoU0aTiqAMua24iWD9GPMXTexUZrP0zXhquHjquPZs3fx5DlV0O40ZcBm +LC/0Btm2k70AEmO1G77MV/udBSRc42vdjsWCoTUWoCHPL8NF2pl2Qidrw6DtQJFc +EXros3krLZSX7TKR6qP5l8oiNBMBRHx5f7VJv8RMLKxkdq4l6RCrbgkV9Rvqu70p +5vc8f2/Xf3lyO6JzxjdPhc9YGjp3bqLAFL0TY7gCoU0mV02ppWb7TSSYMLVGKYDa +P8Xt1aNVAgMBAAECggEAe0fdIEiCAJ4AEzTgzRwmO5B5gbtbldiDZUPTAoC1SYF7 +v/DEsNSo6BSzptkRULkOW7Y7Vr13Tjl28hM7e+ySmP2JrQIMqP1yP2MzC1PBMIsV +dIeLdsuAA+YzMaRi9ShqqE58ti+UMnFSBa1BEiwJK7oJZHq8W7H7PicCSjpfG3Tu +CiDxBYTRw7G0asfCyT0wNDaWbfzVxCq6O0S6FxbBZFCM1ffQfEe5t0IaPXKjRBWi +qHprRdmgP6m1xmq1pDnImRri1a8DNH8GaV+eEWJXhhtsajMOaK3tJ2QDNxCuqWQf +PaOJEzX1uyBwnszYaTqeVfZerQGSdQxdnxIVT3FiAQKBgQDSroQV4LbcnjG7XbOl +nGy2OycCfBOmoYLcKTUPvBBpkbX222e6k9WwWokVwpL7XZhxEZsbdf9GrI8q/3wl +WgZKG2Whrl7r/Y9t0TqpG6sETaAcGK0aSmThP1lEPes6QyqnlNVTnCbpDYGUL+wg +3uCV0i8CorPnI7lJxZmUk8XxlQKBgQDC+32zINK07DdTRcnb0uXXgmAQtSObzVcS +Z/UsPZBRPnJKwZegv6eGZzisgVMDNN42ttkuvOsY+eTshbWpIOZsC6MvDmrFomFY +jBDtsTb4vZDjpCM3naN8NbtPfLhPzigN6Unu3eBWrNpu98tOCJcKCsKcK8R1GnY4 +teXL+zP6wQKBgQCE/55qoT1O8J68hV6PQbxvHdB2hRVyr6sIiQryL+q8ejvA5qkI +46KWfdDkZ7yZ6m/9kPu1zjukxfJrVnVsrvQhfEIgWw+ZMqyEyvyZHNpNESPuGLXO +TwB4IaGcrun8EcKvoAk7iuwVW21duxL5fTmR2unKZCPl2mtx4sgdP1xKMQKBgHqx +M/S+x7fvGBU/SfkYaWbF9FsvEHwepxa+8cjn6GMnL1YZIaeWUf1CAKiULD9NSjMV +te4yFKG/xayLqrcFK9l7LTEUGl3IgwbsBE79f4DgEEceZsa9XD1xti8bZQNaz1uJ +m+tCFMOVu1GufXDZ+OjpwnHxHIsvnoqq8zH7HUdBAoGANUeyzsm9COfHkqulOFSl +70/By/gX6VeTaN8AMk8HPeqhCgMycZ88RDlvZTA5Rkpe5F9pBtvPc7X6kMD/Svrv +V15RJkrxViubEQJWFWHIvuu9LIHCXtsvktxI5W0rL+YZ07vd01eroEgMsmTvTaDw +P8gcuwgcOi7M1NQzOc8MAb0= +-----END PRIVATE KEY----- diff -r 5ad5efc847c7 Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py Mon Dec 10 00:03:55 2012 +0200 +++ b/Lib/test/test_ssl.py Mon Dec 10 14:26:43 2012 +1100 @@ -37,6 +37,9 @@ # are meant to authenticate. CERTFILE = data_file("keycert.pem") +CERTFILE3 = data_file("keycert3.pem") +CERTFILE4 = data_file("keycert4.pem") +CA = data_file("pycacert.pem") BYTES_CERTFILE = os.fsencode(CERTFILE) ONLYCERT = data_file("ssl_cert.pem") ONLYKEY = data_file("ssl_key.pem") @@ -142,9 +145,10 @@ (('organizationName', 'Python Software Foundation'),), (('commonName', 'localhost'),)) ) - self.assertEqual(p['notAfter'], 'Oct 5 23:01:56 2020 GMT') - self.assertEqual(p['notBefore'], 'Oct 8 23:01:56 2010 GMT') - self.assertEqual(p['serialNumber'], 'D7C7381919AFC24E') + # Note the next three asserts will fail if the keys are regenerated + #self.assertEqual(p['notAfter'], 'Oct 5 23:01:56 2020 GMT') + #self.assertEqual(p['notBefore'], 'Oct 8 23:01:56 2010 GMT') + #self.assertEqual(p['serialNumber'], 'D7C7381919AFC24E') self.assertEqual(p['subject'], ((('countryName', 'XY'),), (('localityName', 'Castle Anthrax'),), @@ -1238,7 +1242,7 @@ 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): + chatty=True, connectionchatty=False, sni_name=None): """ Launch a server, connect a client to it and try various reads and writes. @@ -1248,7 +1252,8 @@ chatty=chatty, connectionchatty=False) with server: - with client_context.wrap_socket(socket.socket()) as s: + with client_context.wrap_socket(socket.socket(), + server_hostname=sni_name) as s: s.connect((HOST, server.port)) for arg in [indata, bytearray(indata), memoryview(indata)]: if connectionchatty: @@ -1272,6 +1277,7 @@ stats.update({ 'compression': s.compression(), 'cipher': s.cipher(), + 'peercert': s.getpeercert(), 'client_npn_protocol': s.selected_npn_protocol() }) s.close() @@ -1977,6 +1983,64 @@ if len(stats['server_npn_protocols']) else 'nothing' self.assertEqual(server_result, expected, msg % (server_result, "server")) + @unittest.skipUnless(ssl.HAS_SNI, "SNI support needed for this test") + def test_sni(self): + hostname = "funny" + def servername(s,servername,oldctx): + nonlocal hostname + newctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + newctx.load_cert_chain(CERTFILE4) + s.context = newctx + hostname = servername + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + + self.assertRaises(TypeError, server_context.set_servername_callback, 4) + self.assertRaises(TypeError, server_context.set_servername_callback, "This definately isn't a function") + self.assertRaises(TypeError, server_context.set_servername_callback, server_context) + + server_context.set_servername_callback(servername) + server_context.load_cert_chain(CERTFILE3) + + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + client_context.verify_mode = ssl.CERT_REQUIRED + client_context.load_verify_locations(CA) + stats = server_params_test(client_context, server_context, + chatty=True, + connectionchatty=True, + sni_name='supermessage') + + self.assertEqual(hostname, "supermessage", + "sni server didn't get hostname 'supermessage' got %s" % hostname) + + def certreceived(stats, name): + try: + cert = stats['peercert'] + self.assertTrue( (('commonName',name),) in cert['subject'], + "Callback didn't change certificate got %s" % pprint.pformat(cert)) + except KeyError: + raise AssertionError('[peercert][subject] missing %s' % pprint.pformat('stat')) + + certreceived(stats, 'fakehostname') + + hostname = "funny" + server_context.set_servername_callback(None) + + server_context.load_cert_chain(CERTFILE3) + stats = server_params_test(client_context, server_context, + chatty=True, + connectionchatty=True, + sni_name='notfunny') + self.assertEqual(hostname, "funny", + "sni server callback was disabled. hostname changed and it shouldn't have") + + certreceived(stats, 'localhost') + + def dummycallback(s, name, ctx): + pass + + # Check our object reference counting + server_context.set_servername_callback(dummycallback) def test_main(verbose=False): if support.verbose: @@ -1999,6 +2063,7 @@ for filename in [ CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, BYTES_CERTFILE, + CA, CERTFILE3, CERTFILE4, ONLYCERT, ONLYKEY, BYTES_ONLYCERT, BYTES_ONLYKEY, BADCERT, BADKEY, EMPTYCERT]: if not os.path.exists(filename): diff -r 5ad5efc847c7 Modules/_ssl.c --- a/Modules/_ssl.c Mon Dec 10 00:03:55 2012 +0200 +++ b/Modules/_ssl.c Mon Dec 10 14:26:43 2012 +1100 @@ -181,12 +181,18 @@ char *npn_protocols; int npn_protocols_len; #endif +#ifndef OPENSSL_NO_TLSEXT + PyObject *set_hostname; +#endif } PySSLContext; typedef struct { PyObject_HEAD PyObject *Socket; /* weakref to socket on which we're layered */ SSL *ssl; +#ifndef OPENSSL_NO_TLSEXT + PySSLContext *ctx; /* weakref to SSL context */ +#endif X509 *peer_cert; int shutdown_seen_zero; enum py_ssl_server_or_client socket_type; @@ -437,11 +443,12 @@ */ static PySSLSocket * -newPySSLSocket(SSL_CTX *ctx, PySocketSockObject *sock, +newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, enum py_ssl_server_or_client socket_type, char *server_hostname) { PySSLSocket *self; + SSL_CTX *ctx = sslctx->ctx; self = PyObject_New(PySSLSocket, &PySSLSocket_Type); if (self == NULL) @@ -450,6 +457,10 @@ self->peer_cert = NULL; self->ssl = NULL; self->Socket = NULL; +#ifndef OPENSSL_NO_TLSEXT + self->ctx = sslctx; + Py_INCREF(sslctx); +#endif /* Make sure the SSL error state is initialized */ (void) ERR_get_state(); @@ -458,6 +469,7 @@ PySSL_BEGIN_ALLOW_THREADS self->ssl = SSL_new(ctx); PySSL_END_ALLOW_THREADS + SSL_set_app_data(self->ssl,self); SSL_set_fd(self->ssl, sock->sock_fd); #ifdef SSL_MODE_AUTO_RETRY SSL_set_mode(self->ssl, SSL_MODE_AUTO_RETRY); @@ -1164,6 +1176,41 @@ #endif } +static PySSLContext *PySSL_get_context(PySSLSocket *self, void *closure) { + Py_INCREF(self->ctx); + return self->ctx; +} + +static int PySSL_set_context(PySSLSocket *self, PyObject *value, + void *closure) { + + if (self->ssl) { + + if (PyObject_TypeCheck(value, &PySSLContext_Type)) { + + Py_DECREF(self->ctx); + Py_INCREF(value); + self->ctx = (PySSLContext *) value; + SSL_set_SSL_CTX(self->ssl, self->ctx->ctx); + } else { + PyErr_SetString(PyExc_TypeError, "The value must be a SSLContext"); + return -1; + } + } + + return 0; +} + +PyDoc_STRVAR(PySSL_set_context_doc, +"_setter_context(ctx)\n\ +\ +This changes the context associated with the SSLSocket. This is typically\n\ +used from within a callback function set by the set_servername_callback\n\ +on the SSLContext to change the certificate information associated with the\n\ +SSLSocket before the cryptographic exchange handshake messages\n"); + + + static void PySSL_dealloc(PySSLSocket *self) { if (self->peer_cert) /* Possible not to have one? */ @@ -1171,6 +1218,7 @@ if (self->ssl) SSL_free(self->ssl); Py_XDECREF(self->Socket); + Py_XDECREF(self->ctx); PyObject_Del(self); } @@ -1606,6 +1654,12 @@ #endif /* HAVE_OPENSSL_FINISHED */ +static PyGetSetDef ssl_getsetlist[] = { + {"context", (getter) PySSL_get_context, + (setter) PySSL_set_context, PySSL_set_context_doc}, + {NULL}, /* sentinel */ +}; + static PyMethodDef PySSLMethods[] = { {"do_handshake", (PyCFunction)PySSL_SSLdo_handshake, METH_NOARGS}, {"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS, @@ -1660,6 +1714,8 @@ 0, /*tp_iter*/ 0, /*tp_iternext*/ PySSLMethods, /*tp_methods*/ + 0, /*tp_members*/ + ssl_getsetlist, /*tp_getset*/ }; @@ -1716,6 +1772,9 @@ #ifdef OPENSSL_NPN_NEGOTIATED self->npn_protocols = NULL; #endif +#ifndef OPENSSL_NO_TLSEXT + self->set_hostname = NULL; +#endif /* Defaults */ SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL); SSL_CTX_set_options(self->ctx, @@ -1736,6 +1795,9 @@ #ifdef OPENSSL_NPN_NEGOTIATED PyMem_Free(self->npn_protocols); #endif +#ifndef OPENSSL_NO_TLSEXT + Py_XDECREF(self->set_hostname); +#endif Py_TYPE(self)->tp_free(self); } @@ -2223,7 +2285,7 @@ #endif } - res = (PyObject *) newPySSLSocket(self->ctx, sock, server_side, + res = (PyObject *) newPySSLSocket(self, sock, server_side, hostname); if (hostname != NULL) PyMem_Free(hostname); @@ -2308,6 +2370,116 @@ } #endif +#ifndef OPENSSL_NO_TLSEXT +static int _servername_callback(SSL *s, int *al, void *args) { + int ret = SSL_TLSEXT_ERR_OK; + PySSLContext *ssl_ctx = (PySSLContext *) args; + PySSLSocket *ssl; + PyObject *servername_o; + PyObject *servername_idna; + PyObject *result; + PyGILState_STATE gstate; + const char * servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); + + ssl = SSL_get_app_data(s); + + if (ssl_ctx->set_hostname == NULL) { + /* remove race condition in this the call back while if removing the + * callback is in progress */ + return ret; + } + + gstate = PyGILState_Ensure(); + + servername_o = PyBytes_FromString(servername); + if (servername_o == NULL) { + PyErr_WriteUnraisable((PyObject *) ssl_ctx); + goto error; + } + servername_idna = PyUnicode_FromEncodedObject(servername_o,"idna",NULL); + if (servername_idna == NULL) { + PyErr_WriteUnraisable(servername_o); + Py_DECREF(servername_o); + goto error; + } + Py_DECREF(servername_o); + result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl, + servername_idna, ssl_ctx, NULL); + Py_DECREF(servername_idna); + + if (result == NULL) { + PyErr_WriteUnraisable(ssl_ctx->set_hostname); + *al = SSL_AD_HANDSHAKE_FAILURE; + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + } else { + if (result != Py_None) { + if (PyLong_Check(result)) { + *al = (int) PyLong_AsLong(result); + } else { + PyErr_WriteUnraisable(result); + *al = SSL_AD_INTERNAL_ERROR; + } + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + } + Py_DECREF(result); + } + + PyGILState_Release(gstate); + return ret; + +error: + PyGILState_Release(gstate); + *al = SSL_AD_INTERNAL_ERROR; + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + return ret; +} + +PyDoc_STRVAR(PySSL_set_servername_callback_doc, +"set_servername(method)\n\ +\ +This sets a callback that will be called when a server name is provided by\n\ +the SSL/TLS client in the SNI extension.\n\ +\ +If the argument is None then the callback is disabled. The method is called\n\ +with the SSLSocket, the server name as a string, and the SSLContext object.\n\ +See RFC 6066 for details of the SNI"); +#endif + +static PyObject * +set_servername_callback(PySSLContext *self, PyObject *args) +{ +#ifndef OPENSSL_NO_TLSEXT + PyObject *o = self->set_hostname; + + if (!PyArg_ParseTuple(args, "O", &self->set_hostname)) { + self->set_hostname = o; + return NULL; + } + + if (self->set_hostname == Py_None) { + SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); + self->set_hostname = NULL; + } else { + Py_INCREF(self->set_hostname); + if (!PyCallable_Check(self->set_hostname)) { + Py_DECREF(self->set_hostname); + SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); + self->set_hostname = o; + PyErr_SetString(PyExc_TypeError, + "not a callable object"); + return NULL; + } + SSL_CTX_set_tlsext_servername_callback(self->ctx, _servername_callback); + SSL_CTX_set_tlsext_servername_arg(self->ctx, self); + } + Py_XDECREF(o); + Py_RETURN_NONE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "The TLS extension servername callback, SSL_CTX_set_tlsext_servername_callback, is not in the current OpenSSL library."); +#endif +} + static PyGetSetDef context_getsetlist[] = { {"options", (getter) get_options, (setter) set_options, NULL}, @@ -2337,6 +2509,8 @@ {"set_ecdh_curve", (PyCFunction) set_ecdh_curve, METH_O, NULL}, #endif + {"set_servername_callback", (PyCFunction) set_servername_callback, + METH_VARARGS, PySSL_set_servername_callback_doc}, {NULL, NULL} /* sentinel */ }; @@ -2743,6 +2917,65 @@ PyModule_AddIntConstant(m, "CERT_REQUIRED", PY_SSL_CERT_REQUIRED); + /* Alert Descriptions from ssl.h */ + /* note RESERVED constants no longer intended for use have been removed */ + /* http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6 */ + + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_CLOSE_NOTIFY", + SSL_AD_CLOSE_NOTIFY); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_UNEXPECTED_MESSAGE", + SSL_AD_UNEXPECTED_MESSAGE); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_BAD_RECORD_MAC", + SSL_AD_BAD_RECORD_MAC); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_RECORD_OVERFLOW", + SSL_AD_RECORD_OVERFLOW); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_DECOMPRESSION_FAILURE", + SSL_AD_DECOMPRESSION_FAILURE); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_HANDSHAKE_FAILURE", + SSL_AD_HANDSHAKE_FAILURE); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_BAD_CERTIFICATE", + SSL_AD_BAD_CERTIFICATE); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE", + SSL_AD_UNSUPPORTED_CERTIFICATE); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_CERTIFICATE_REVOKED", + SSL_AD_CERTIFICATE_REVOKED); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_CERTIFICATE_EXPIRED", + SSL_AD_CERTIFICATE_EXPIRED); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN", + SSL_AD_CERTIFICATE_UNKNOWN); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_ILLEGAL_PARAMETER", + SSL_AD_ILLEGAL_PARAMETER); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_UNKNOWN_CA", + SSL_AD_UNKNOWN_CA); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_ACCESS_DENIED", + SSL_AD_ACCESS_DENIED); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_DECODE_ERROR", + SSL_AD_DECODE_ERROR); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_DECRYPT_ERROR", + SSL_AD_DECRYPT_ERROR); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_PROTOCOL_VERSION", + SSL_AD_PROTOCOL_VERSION); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_INSUFFICIENT_SECURITY", + SSL_AD_INSUFFICIENT_SECURITY); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_INTERNAL_ERROR", + SSL_AD_INTERNAL_ERROR); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_USER_CANCELLED", + SSL_AD_USER_CANCELLED); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_NO_RENEGOTIATION", + SSL_AD_NO_RENEGOTIATION); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION", + SSL_AD_UNSUPPORTED_EXTENSION); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE", + SSL_AD_CERTIFICATE_UNOBTAINABLE); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_UNRECOGNIZED_NAME", + SSL_AD_UNRECOGNIZED_NAME); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE", + SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE", + SSL_AD_BAD_CERTIFICATE_HASH_VALUE); + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY", + SSL_AD_UNKNOWN_PSK_IDENTITY); + /* protocol versions */ #ifndef OPENSSL_NO_SSL2 PyModule_AddIntConstant(m, "PROTOCOL_SSLv2",