test_tls.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. # Copyright 2019 New Vector Ltd
  2. # Copyright 2019 Matrix.org Foundation C.I.C.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import idna
  16. from OpenSSL import SSL
  17. from synapse.config._base import Config, RootConfig
  18. from synapse.config.tls import ConfigError, TlsConfig
  19. from synapse.crypto.context_factory import FederationPolicyForHTTPS
  20. from tests.unittest import TestCase
  21. class FakeServer(Config):
  22. section = "server"
  23. def has_tls_listener(self):
  24. return False
  25. class TestConfig(RootConfig):
  26. config_classes = [FakeServer, TlsConfig]
  27. class TLSConfigTests(TestCase):
  28. def test_tls_client_minimum_default(self):
  29. """
  30. The default client TLS version is 1.0.
  31. """
  32. config = {}
  33. t = TestConfig()
  34. t.read_config(config, config_dir_path="", data_dir_path="")
  35. self.assertEqual(t.federation_client_minimum_tls_version, "1")
  36. def test_tls_client_minimum_set(self):
  37. """
  38. The default client TLS version can be set to 1.0, 1.1, and 1.2.
  39. """
  40. config = {"federation_client_minimum_tls_version": 1}
  41. t = TestConfig()
  42. t.read_config(config, config_dir_path="", data_dir_path="")
  43. self.assertEqual(t.federation_client_minimum_tls_version, "1")
  44. config = {"federation_client_minimum_tls_version": 1.1}
  45. t = TestConfig()
  46. t.read_config(config, config_dir_path="", data_dir_path="")
  47. self.assertEqual(t.federation_client_minimum_tls_version, "1.1")
  48. config = {"federation_client_minimum_tls_version": 1.2}
  49. t = TestConfig()
  50. t.read_config(config, config_dir_path="", data_dir_path="")
  51. self.assertEqual(t.federation_client_minimum_tls_version, "1.2")
  52. # Also test a string version
  53. config = {"federation_client_minimum_tls_version": "1"}
  54. t = TestConfig()
  55. t.read_config(config, config_dir_path="", data_dir_path="")
  56. self.assertEqual(t.federation_client_minimum_tls_version, "1")
  57. config = {"federation_client_minimum_tls_version": "1.2"}
  58. t = TestConfig()
  59. t.read_config(config, config_dir_path="", data_dir_path="")
  60. self.assertEqual(t.federation_client_minimum_tls_version, "1.2")
  61. def test_tls_client_minimum_1_point_3_missing(self):
  62. """
  63. If TLS 1.3 support is missing and it's configured, it will raise a
  64. ConfigError.
  65. """
  66. # thanks i hate it
  67. if hasattr(SSL, "OP_NO_TLSv1_3"):
  68. OP_NO_TLSv1_3 = SSL.OP_NO_TLSv1_3
  69. delattr(SSL, "OP_NO_TLSv1_3")
  70. self.addCleanup(setattr, SSL, "SSL.OP_NO_TLSv1_3", OP_NO_TLSv1_3)
  71. assert not hasattr(SSL, "OP_NO_TLSv1_3")
  72. config = {"federation_client_minimum_tls_version": 1.3}
  73. t = TestConfig()
  74. with self.assertRaises(ConfigError) as e:
  75. t.read_config(config, config_dir_path="", data_dir_path="")
  76. self.assertEqual(
  77. e.exception.args[0],
  78. (
  79. "federation_client_minimum_tls_version cannot be 1.3, "
  80. "your OpenSSL does not support it"
  81. ),
  82. )
  83. def test_tls_client_minimum_1_point_3_exists(self):
  84. """
  85. If TLS 1.3 support exists and it's configured, it will be settable.
  86. """
  87. # thanks i hate it, still
  88. if not hasattr(SSL, "OP_NO_TLSv1_3"):
  89. SSL.OP_NO_TLSv1_3 = 0x00
  90. self.addCleanup(lambda: delattr(SSL, "OP_NO_TLSv1_3"))
  91. assert hasattr(SSL, "OP_NO_TLSv1_3")
  92. config = {"federation_client_minimum_tls_version": 1.3}
  93. t = TestConfig()
  94. t.read_config(config, config_dir_path="", data_dir_path="")
  95. self.assertEqual(t.federation_client_minimum_tls_version, "1.3")
  96. def test_tls_client_minimum_set_passed_through_1_2(self):
  97. """
  98. The configured TLS version is correctly configured by the ContextFactory.
  99. """
  100. config = {"federation_client_minimum_tls_version": 1.2}
  101. t = TestConfig()
  102. t.read_config(config, config_dir_path="", data_dir_path="")
  103. cf = FederationPolicyForHTTPS(t)
  104. options = _get_ssl_context_options(cf._verify_ssl_context)
  105. # The context has had NO_TLSv1_1 and NO_TLSv1_0 set, but not NO_TLSv1_2
  106. self.assertNotEqual(options & SSL.OP_NO_TLSv1, 0)
  107. self.assertNotEqual(options & SSL.OP_NO_TLSv1_1, 0)
  108. self.assertEqual(options & SSL.OP_NO_TLSv1_2, 0)
  109. def test_tls_client_minimum_set_passed_through_1_0(self):
  110. """
  111. The configured TLS version is correctly configured by the ContextFactory.
  112. """
  113. config = {"federation_client_minimum_tls_version": 1}
  114. t = TestConfig()
  115. t.read_config(config, config_dir_path="", data_dir_path="")
  116. cf = FederationPolicyForHTTPS(t)
  117. options = _get_ssl_context_options(cf._verify_ssl_context)
  118. # The context has not had any of the NO_TLS set.
  119. self.assertEqual(options & SSL.OP_NO_TLSv1, 0)
  120. self.assertEqual(options & SSL.OP_NO_TLSv1_1, 0)
  121. self.assertEqual(options & SSL.OP_NO_TLSv1_2, 0)
  122. def test_whitelist_idna_failure(self):
  123. """
  124. The federation certificate whitelist will not allow IDNA domain names.
  125. """
  126. config = {
  127. "federation_certificate_verification_whitelist": [
  128. "example.com",
  129. "*.ドメイン.テスト",
  130. ]
  131. }
  132. t = TestConfig()
  133. e = self.assertRaises(
  134. ConfigError, t.read_config, config, config_dir_path="", data_dir_path=""
  135. )
  136. self.assertIn("IDNA domain names", str(e))
  137. def test_whitelist_idna_result(self):
  138. """
  139. The federation certificate whitelist will match on IDNA encoded names.
  140. """
  141. config = {
  142. "federation_certificate_verification_whitelist": [
  143. "example.com",
  144. "*.xn--eckwd4c7c.xn--zckzah",
  145. ]
  146. }
  147. t = TestConfig()
  148. t.read_config(config, config_dir_path="", data_dir_path="")
  149. cf = FederationPolicyForHTTPS(t)
  150. # Not in the whitelist
  151. opts = cf.get_options(b"notexample.com")
  152. self.assertTrue(opts._verifier._verify_certs)
  153. # Caught by the wildcard
  154. opts = cf.get_options(idna.encode("テスト.ドメイン.テスト"))
  155. self.assertFalse(opts._verifier._verify_certs)
  156. def _get_ssl_context_options(ssl_context: SSL.Context) -> int:
  157. """get the options bits from an openssl context object"""
  158. # the OpenSSL.SSL.Context wrapper doesn't expose get_options, so we have to
  159. # use the low-level interface
  160. return SSL._lib.SSL_CTX_get_options(ssl_context._context)