summaryrefslogtreecommitdiffstats
path: root/test/modules/http2/test_101_ssl_reneg.py
blob: d278af21ed456bc8d8fb5f54f1e0f5f1da252bc7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import re
import pytest

from .env import H2Conf, H2TestEnv


@pytest.mark.skipif(condition=H2TestEnv.is_unsupported, reason="mod_http2 not supported here")
@pytest.mark.skipif(H2TestEnv.get_ssl_module() != "mod_ssl", reason="only for mod_ssl")
class TestSslRenegotiation:

    @pytest.fixture(autouse=True, scope='class')
    def _class_scope(self, env):
        domain = f"ssl.{env.http_tld}"
        conf = H2Conf(env, extras={
            'base': [
                "SSLCipherSuite ECDHE-RSA-AES256-GCM-SHA384",
                f"<Directory \"{env.server_dir}/htdocs/ssl-client-verify\">",
                "    Require all granted",
                "    SSLVerifyClient require",
                "    SSLVerifyDepth 0",
                "</Directory>"
            ],
            domain: [
                "Protocols h2 http/1.1",
                "<Location /renegotiate/cipher>",
                "    SSLCipherSuite ECDHE-RSA-CHACHA20-POLY1305",
                "</Location>",
                "<Location /renegotiate/err-doc-cipher>",
                "    SSLCipherSuite ECDHE-RSA-CHACHA20-POLY1305",
                "    ErrorDocument 403 /forbidden.html",
                "</Location>",
                "<Location /renegotiate/verify>",
                "    SSLVerifyClient require",
                "</Location>",
                f"<Directory \"{env.server_dir}/htdocs/sslrequire\">",
                "    SSLRequireSSL",
                "</Directory>",
                f"<Directory \"{env.server_dir}/htdocs/requiressl\">",
                "    Require ssl",
                "</Directory>",
        ]})
        conf.add_vhost(domains=[domain], port=env.https_port,
                       doc_root=f"{env.server_dir}/htdocs")
        conf.install()
        # the dir needs to exists for the configuration to have effect
        env.mkpath("%s/htdocs/ssl-client-verify" % env.server_dir)
        env.mkpath("%s/htdocs/renegotiate/cipher" % env.server_dir)
        env.mkpath("%s/htdocs/sslrequire" % env.server_dir)
        env.mkpath("%s/htdocs/requiressl" % env.server_dir)
        assert env.apache_restart() == 0

    # access a resource with SSL renegotiation, using HTTP/1.1
    def test_h2_101_01(self, env):
        url = env.mkurl("https", "ssl", "/renegotiate/cipher/")
        r = env.curl_get(url, options=["-v", "--http1.1", "--tlsv1.2", "--tls-max", "1.2"])
        assert 0 == r.exit_code, f"{r}"
        assert r.response
        assert 403 == r.response["status"]
        #
        env.httpd_error_log.ignore_recent(
            lognos = [
                "AH01276" # No matching DirectoryIndex found
            ]
        )
        
    # try to renegotiate the cipher, should fail with correct code
    def test_h2_101_02(self, env):
        if not (env.curl_is_at_least('8.2.0') or env.curl_is_less_than('8.1.0')):
            pytest.skip("need curl != 8.1.x version")
        url = env.mkurl("https", "ssl", "/renegotiate/cipher/")
        r = env.curl_get(url, options=[
            "-vvv", "--tlsv1.2", "--tls-max", "1.2", "--ciphers", "ECDHE-RSA-AES256-GCM-SHA384"
        ])
        assert 0 != r.exit_code
        assert not r.response
        assert re.search(r'HTTP_1_1_REQUIRED \(err 13\)', r.stderr)
        #
        env.httpd_error_log.ignore_recent(
            lognos = [
                "AH02261"   # Re-negotiation handshake failed
            ],
            matches = [
                r'.*:tls_post_process_client_hello:.*',
                r'.*SSL Library Error:.*:SSL routines::no shared cipher.*'
            ]
        )
        
    # try to renegotiate a client certificate from Location 
    # needs to fail with correct code
    def test_h2_101_03(self, env):
        if not (env.curl_is_at_least('8.2.0') or env.curl_is_less_than('8.1.0')):
            pytest.skip("need curl != 8.1.x version")
        url = env.mkurl("https", "ssl", "/renegotiate/verify/")
        r = env.curl_get(url, options=["-vvv", "--tlsv1.2", "--tls-max", "1.2"])
        assert 0 != r.exit_code
        assert not r.response
        assert re.search(r'HTTP_1_1_REQUIRED \(err 13\)', r.stderr)
        #
        env.httpd_error_log.ignore_recent(
            lognos = [
                "AH02261"   # Re-negotiation handshake failed
            ],
            matches = [
                r'.*:tls_process_client_certificate:.*',
                r'.*SSL Library Error:.*:SSL routines::peer did not return a certificate.*'
            ]
        )
        
    # try to renegotiate a client certificate from Directory 
    # needs to fail with correct code
    def test_h2_101_04(self, env):
        if not (env.curl_is_at_least('8.2.0') or env.curl_is_less_than('8.1.0')):
            pytest.skip("need curl != 8.1.x version")
        url = env.mkurl("https", "ssl", "/ssl-client-verify/index.html")
        r = env.curl_get(url, options=["-vvv", "--tlsv1.2", "--tls-max", "1.2"])
        assert 0 != r.exit_code, f"{r}"
        assert not r.response
        assert re.search(r'HTTP_1_1_REQUIRED \(err 13\)', r.stderr)
        #
        env.httpd_error_log.ignore_recent(
            lognos = [
                "AH02261"   # Re-negotiation handshake failed
            ],
            matches = [
                r'.*:tls_process_client_certificate:.*',
                r'.*SSL Library Error:.*:SSL routines::peer did not return a certificate.*'
            ]
        )
        
    # make 10 requests on the same connection, none should produce a status code
    # reported by erki@example.ee
    def test_h2_101_05(self, env):
        r = env.run([env.h2load, "-n", "10", "-c", "1", "-m", "1", "-vvvv",
                     f"{env.https_base_url}/ssl-client-verify/index.html"])
        assert 0 == r.exit_code
        r = env.h2load_status(r)
        assert 10 == r.results["h2load"]["requests"]["total"]
        assert 10 == r.results["h2load"]["requests"]["started"]
        assert 10 == r.results["h2load"]["requests"]["done"]
        assert 0 == r.results["h2load"]["requests"]["succeeded"]
        assert 0 == r.results["h2load"]["status"]["2xx"]
        assert 0 == r.results["h2load"]["status"]["3xx"]
        assert 0 == r.results["h2load"]["status"]["4xx"]
        assert 0 == r.results["h2load"]["status"]["5xx"]

    # Check that "SSLRequireSSL" works on h2 connections
    # See <https://bz.apache.org/bugzilla/show_bug.cgi?id=62654>
    def test_h2_101_10a(self, env):
        url = env.mkurl("https", "ssl", "/sslrequire/index.html")
        r = env.curl_get(url)
        assert 0 == r.exit_code
        assert r.response
        assert 404 == r.response["status"]

    # Check that "require ssl" works on h2 connections
    # See <https://bz.apache.org/bugzilla/show_bug.cgi?id=62654>
    def test_h2_101_10b(self, env):
        url = env.mkurl("https", "ssl", "/requiressl/index.html")
        r = env.curl_get(url)
        assert 0 == r.exit_code
        assert r.response
        assert 404 == r.response["status"]
        
    # Check that status works with ErrorDoc, see pull #174, fixes #172
    def test_h2_101_11(self, env):
        if not (env.curl_is_at_least('8.2.0') or env.curl_is_less_than('8.1.0')):
            pytest.skip("need curl != 8.1.x version")
        url = env.mkurl("https", "ssl", "/renegotiate/err-doc-cipher")
        r = env.curl_get(url, options=[
            "-vvv", "--tlsv1.2", "--tls-max", "1.2", "--ciphers", "ECDHE-RSA-AES256-GCM-SHA384"
        ])
        assert 0 != r.exit_code
        assert not r.response
        assert re.search(r'HTTP_1_1_REQUIRED \(err 13\)', r.stderr)
        #
        env.httpd_error_log.ignore_recent(
            lognos = [
                "AH02261"   # Re-negotiation handshake failed
            ],
            matches = [
                r'.*:tls_post_process_client_hello:.*',
                r'.*SSL Library Error:.*:SSL routines::no shared cipher.*'
            ]
        )