cve-2024-38477/httpd-2.4.59/test/modules/tls/test_12_cauth.py
2025-06-05 15:09:30 +02:00

236 lines
9.1 KiB
Python

import os
from datetime import timedelta
from typing import Optional
import pytest
from pyhttpd.certs import Credentials
from .conf import TlsTestConf
@pytest.fixture
def clients_x(env):
return env.ca.get_first("clientsX")
@pytest.fixture
def clients_y(env):
return env.ca.get_first("clientsY")
@pytest.fixture
def cax_file(clients_x):
return os.path.join(os.path.dirname(clients_x.cert_file), "clientX-ca.pem")
@pytest.mark.skip(reason="client certs disabled")
class TestTLS:
@pytest.fixture(autouse=True, scope='class')
def _class_scope(self, env, clients_x, cax_file):
with open(cax_file, 'w') as fd:
fd.write("".join(open(clients_x.cert_file).readlines()))
fd.write("".join(open(env.ca.cert_file).readlines()))
@pytest.fixture(autouse=True, scope='function')
def _function_scope(self, env):
if env.is_live(timeout=timedelta(milliseconds=100)):
assert env.apache_stop() == 0
def get_ssl_var(self, env, domain: str, cert: Optional[Credentials], name: str):
r = env.tls_get(domain, f"/vars.py?name={name}", options=[
"--cert", cert.cert_file
] if cert else [])
assert r.exit_code == 0, r.stderr
assert r.json, r.stderr + r.stdout
return r.json[name] if name in r.json else None
def test_tls_12_set_ca_non_existing(self, env):
conf = TlsTestConf(env=env, extras={
env.domain_a: "TLSClientCA xxx"
})
conf.add_md_vhosts(domains=[env.domain_a, env.domain_b])
conf.install()
assert env.apache_restart() == 1
def test_tls_12_set_ca_existing(self, env, cax_file):
conf = TlsTestConf(env=env, extras={
env.domain_a: f"TLSClientCA {cax_file}"
})
conf.add_md_vhosts(domains=[env.domain_a, env.domain_b])
conf.install()
assert env.apache_restart() == 0
def test_tls_12_set_auth_no_ca(self, env):
conf = TlsTestConf(env=env, extras={
env.domain_a: "TLSClientCertificate required"
})
conf.add_md_vhosts(domains=[env.domain_a, env.domain_b])
conf.install()
# will fail bc lacking clien CA
assert env.apache_restart() == 1
def test_tls_12_auth_option_std(self, env, cax_file, clients_x):
conf = TlsTestConf(env=env, extras={
env.domain_b: [
f"TLSClientCertificate required",
f"TLSClientCA {cax_file}",
"# TODO: TLSUserName SSL_CLIENT_S_DN_CN",
"TLSOptions +StdEnvVars",
]
})
conf.add_md_vhosts(domains=[env.domain_b])
conf.install()
assert env.apache_restart() == 0
# should be denied
r = env.tls_get(domain=env.domain_b, paths="/index.json")
assert r.exit_code != 0, r.stdout
# should work
ccert = clients_x.get_first("user1")
data = env.tls_get_json(env.domain_b, "/index.json", options=[
"--cert", ccert.cert_file
])
assert data == {'domain': env.domain_b}
r = env.tls_get(env.domain_b, "/vars.py?name=SSL_CLIENT_S_DN_CN")
assert r.exit_code != 0, "should have been prevented"
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_S_DN_CN")
assert val == 'Not Implemented'
# TODO
# val = self.get_ssl_var(env, env.domain_b, ccert, "REMOTE_USER")
# assert val == 'Not Implemented'
# not set on StdEnvVars, needs option ExportCertData
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_CERT")
assert val == ""
def test_tls_12_auth_option_cert(self, env, test_ca, cax_file, clients_x):
conf = TlsTestConf(env=env, extras={
env.domain_b: [
"TLSClientCertificate required",
f"TLSClientCA {cax_file}",
"TLSOptions Defaults +ExportCertData",
]
})
conf.add_md_vhosts(domains=[env.domain_b])
conf.install()
assert env.apache_restart() == 0
ccert = clients_x.get_first("user1")
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_CERT")
assert val == ccert.cert_pem.decode()
# no chain should be present
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_CHAIN_0")
assert val == ''
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_SERVER_CERT")
assert val
server_certs = test_ca.get_credentials_for_name(env.domain_b)
assert val in [c.cert_pem.decode() for c in server_certs]
def test_tls_12_auth_ssl_optional(self, env, cax_file, clients_x):
domain = env.domain_b
conf = TlsTestConf(env=env, extras={
domain: [
"SSLVerifyClient optional",
"SSLVerifyDepth 2",
"SSLOptions +StdEnvVars +ExportCertData",
f"SSLCACertificateFile {cax_file}",
"SSLUserName SSL_CLIENT_S_DN",
]
})
conf.add_ssl_vhosts(domains=[domain])
conf.install()
assert env.apache_restart() == 0
# should work either way
data = env.tls_get_json(domain, "/index.json")
assert data == {'domain': domain}
# no client cert given, we expect the server variable to be empty
val = self.get_ssl_var(env, env.domain_b, None, "SSL_CLIENT_S_DN_CN")
assert val == ''
ccert = clients_x.get_first("user1")
data = env.tls_get_json(domain, "/index.json", options=[
"--cert", ccert.cert_file
])
assert data == {'domain': domain}
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_S_DN_CN")
assert val == 'user1'
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_S_DN")
assert val == 'O=abetterinternet-mod_tls,OU=clientsX,CN=user1'
val = self.get_ssl_var(env, env.domain_b, ccert, "REMOTE_USER")
assert val == 'O=abetterinternet-mod_tls,OU=clientsX,CN=user1'
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_I_DN")
assert val == 'O=abetterinternet-mod_tls,OU=clientsX'
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_I_DN_CN")
assert val == ''
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_I_DN_OU")
assert val == 'clientsX'
val = self.get_ssl_var(env, env.domain_b, ccert, "SSL_CLIENT_CERT")
assert val == ccert.cert_pem.decode()
def test_tls_12_auth_optional(self, env, cax_file, clients_x):
domain = env.domain_b
conf = TlsTestConf(env=env, extras={
domain: [
"TLSClientCertificate optional",
f"TLSClientCA {cax_file}",
]
})
conf.add_md_vhosts(domains=[domain])
conf.install()
assert env.apache_restart() == 0
# should work either way
data = env.tls_get_json(domain, "/index.json")
assert data == {'domain': domain}
# no client cert given, we expect the server variable to be empty
r = env.tls_get(domain, "/vars.py?name=SSL_CLIENT_S_DN_CN")
assert r.exit_code == 0, r.stderr
assert r.json == {
'SSL_CLIENT_S_DN_CN': '',
}, r.stdout
data = env.tls_get_json(domain, "/index.json", options=[
"--cert", clients_x.get_first("user1").cert_file
])
assert data == {'domain': domain}
r = env.tls_get(domain, "/vars.py?name=SSL_CLIENT_S_DN_CN", options=[
"--cert", clients_x.get_first("user1").cert_file
])
# with client cert, we expect the server variable to show? Do we?
assert r.exit_code == 0, r.stderr
assert r.json == {
'SSL_CLIENT_S_DN_CN': 'Not Implemented',
}, r.stdout
def test_tls_12_auth_expired(self, env, cax_file, clients_x):
conf = TlsTestConf(env=env, extras={
env.domain_b: [
"TLSClientCertificate required",
f"TLSClientCA {cax_file}",
]
})
conf.add_md_vhosts(domains=[env.domain_b])
conf.install()
assert env.apache_restart() == 0
# should not work
r = env.tls_get(domain=env.domain_b, paths="/index.json", options=[
"--cert", clients_x.get_first("user_expired").cert_file
])
assert r.exit_code != 0
def test_tls_12_auth_other_ca(self, env, cax_file, clients_y):
conf = TlsTestConf(env=env, extras={
env.domain_b: [
"TLSClientCertificate required",
f"TLSClientCA {cax_file}",
]
})
conf.add_md_vhosts(domains=[env.domain_b])
conf.install()
assert env.apache_restart() == 0
# should not work
r = env.tls_get(domain=env.domain_b, paths="/index.json", options=[
"--cert", clients_y.get_first("user1").cert_file
])
assert r.exit_code != 0
# This will work, as the CA root is present in the CA file
r = env.tls_get(domain=env.domain_b, paths="/index.json", options=[
"--cert", env.ca.get_first("user1").cert_file
])
assert r.exit_code == 0