Commit 7ff81404 authored by Vincent Pelletier's avatar Vincent Pelletier Committed by Vincent Pelletier

http: Constrain the certificates caucased https CA may sign.

This makes it safer to trust this CA certificate in general-purpose https
clients, like web browsers, as it prevents such trusted CA certificate
from issuing rogue certificates.
Bump pyOpenSSL to latest version (and, as a consequence of pyOpenSSL
18.0.0 itself requiring cryptography 2.1.1, bump it as well) as it seems to
fix a bug related to validating NameConstraints - and anyway fixes
worrying use-after-free errors.
parent 95f2f9fa
...@@ -34,6 +34,7 @@ import tempfile ...@@ -34,6 +34,7 @@ import tempfile
from threading import Thread from threading import Thread
from urlparse import urlparse, urlunsplit from urlparse import urlparse, urlunsplit
from wsgiref.simple_server import make_server, WSGIServer from wsgiref.simple_server import make_server, WSGIServer
import ipaddress
from cryptography import x509 from cryptography import x509
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives import serialization
...@@ -201,7 +202,8 @@ def getSSLContext( ...@@ -201,7 +202,8 @@ def getSSLContext(
key_len, key_len,
threshold, threshold,
server_key_path, server_key_path,
hostname, hostname_ip_address,
hostname_dnsname,
cau, cau,
http_cas, http_cas,
renew=False, # Force renewal when True - used in tests renew=False, # Force renewal when True - used in tests
...@@ -282,7 +284,7 @@ def getSSLContext( ...@@ -282,7 +284,7 @@ def getSSLContext(
subject_name=x509.Name([ subject_name=x509.Name([
x509.NameAttribute( x509.NameAttribute(
oid=x509.oid.NameOID.COMMON_NAME, oid=x509.oid.NameOID.COMMON_NAME,
value=hostname.decode('ascii'), value=hostname_dnsname,
), ),
]), ]),
extensions=[ extensions=[
...@@ -304,7 +306,9 @@ def getSSLContext( ...@@ -304,7 +306,9 @@ def getSSLContext(
), ),
Extension( Extension(
x509.SubjectAlternativeName([ x509.SubjectAlternativeName([
x509.DNSName(hostname.decode('ascii')), x509.DNSName(hostname_dnsname)
if hostname_ip_address is None
else x509.IPAddress(hostname_ip_address),
]), ]),
critical=True, critical=True,
), ),
...@@ -485,6 +489,29 @@ def main(argv=None, until=utils.until): ...@@ -485,6 +489,29 @@ def main(argv=None, until=utils.until):
base_url = u'http://' + args.netloc.decode('ascii') base_url = u'http://' + args.netloc.decode('ascii')
parsed_base_url = urlparse(base_url) parsed_base_url = urlparse(base_url)
hostname = parsed_base_url.hostname hostname = parsed_base_url.hostname
name_constraints_permited = []
name_constraints_excluded = []
hostname_dnsname = hostname.decode('ascii')
try:
hostname_ip_address = ipaddress.ip_address(hostname_dnsname)
except ValueError:
# Hostname is not an ip address, it must be a hostname
name_constraints_permited.append(
x509.DNSName(hostname_dnsname),
)
hostname_ip_address = None
else:
# Hostname is an ip address, forbid hostname claims.
name_constraints_excluded.append(
x509.DNSName(u''),
)
# Convert to a network to meet NameConstraint restrictions.
# Resulting network has maximal prefix length, so it really just covers
# one IP.
name_constraints_permited.append(
x509.IPAddress(ipaddress.ip_network(hostname_ip_address)),
)
http_port = ( http_port = (
parsed_base_url.port parsed_base_url.port
if args.base_port is None if args.base_port is None
...@@ -555,6 +582,15 @@ def main(argv=None, until=utils.until): ...@@ -555,6 +582,15 @@ def main(argv=None, until=utils.until):
ca_subject_dict={ ca_subject_dict={
'CN': u'Caucased CA at ' + https_base_url, 'CN': u'Caucased CA at ' + https_base_url,
}, },
ca_extension_list=[
Extension(
x509.NameConstraints(
permitted_subtrees=name_constraints_permited,
excluded_subtrees=name_constraints_excluded or None,
),
critical=True,
),
],
ca_key_size=args.key_len, ca_key_size=args.key_len,
# This CA certificate will be installed in browser key stores, where # This CA certificate will be installed in browser key stores, where
# automated renewal will be unlikely to happen. As this CA certificate # automated renewal will be unlikely to happen. As this CA certificate
...@@ -614,7 +650,8 @@ def main(argv=None, until=utils.until): ...@@ -614,7 +650,8 @@ def main(argv=None, until=utils.until):
key_len=args.key_len, key_len=args.key_len,
threshold=args.threshold, threshold=args.threshold,
server_key_path=args.server_key, server_key_path=args.server_key,
hostname=hostname, hostname_ip_address=hostname_ip_address,
hostname_dnsname=hostname_dnsname,
cau=cau, cau=cau,
http_cas=http_cas, http_cas=http_cas,
) )
...@@ -657,7 +694,8 @@ def main(argv=None, until=utils.until): ...@@ -657,7 +694,8 @@ def main(argv=None, until=utils.until):
key_len=args.key_len, key_len=args.key_len,
threshold=args.threshold, threshold=args.threshold,
server_key_path=args.server_key, server_key_path=args.server_key,
hostname=hostname, hostname_ip_address=hostname_ip_address,
hostname_dnsname=hostname_dnsname,
cau=cau, cau=cau,
http_cas=http_cas, http_cas=http_cas,
renew=True, renew=True,
......
...@@ -49,8 +49,8 @@ setup( ...@@ -49,8 +49,8 @@ setup(
license='GPLv3+', license='GPLv3+',
packages=find_packages(), packages=find_packages(),
install_requires=[ install_requires=[
'cryptography>=2.1.1', # everything x509 except... 'cryptography>=2.2.1', # everything x509 except...
'pyOpenSSL>=17.1.0', # ...certificate chain validation 'pyOpenSSL>=18.0.0', # ...certificate chain validation
'pem>=17.1.0', # Parse PEM files 'pem>=17.1.0', # Parse PEM files
], ],
zip_safe=True, zip_safe=True,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment