Allow custom domain labels
Original validator checked for domain labels as defined by RFC1034, however real internet deals with other domains as well - starting with digits or symbols. This change allows modifying the pattern to allow custom / relaxed rules. Validation has been removed from adding a domain to a new extension, since it's only used in fixups and the domain should be already validated. (or not, if not configured) Closes-bug: 1592489 Change-Id: Ib453054ba5f554bab28cff392c539e713fa28918
This commit is contained in:
parent
6fdb9be69e
commit
2257b8bbed
@ -24,7 +24,6 @@ from pyasn1.type import tag as asn1_tag
|
||||
from pyasn1.type import univ as asn1_univ
|
||||
|
||||
from anchor.asn1 import rfc5280
|
||||
from anchor import util as a_utils
|
||||
from anchor.X509 import errors
|
||||
from anchor.X509 import utils
|
||||
|
||||
@ -331,11 +330,6 @@ class X509ExtensionSubjectAltName(X509Extension):
|
||||
|
||||
@modifies_ext_value
|
||||
def add_dns_id(self, dns_id, validate=True, ext_value=None):
|
||||
if validate:
|
||||
try:
|
||||
a_utils.verify_domain(dns_id, allow_wildcards=True)
|
||||
except ValueError as e:
|
||||
raise errors.X509Error("invalid domain provided: %s" % str(e))
|
||||
new_pos = len(ext_value)
|
||||
ext_value[new_pos] = None
|
||||
ext_value[new_pos]['dNSName'] = dns_id
|
||||
|
@ -15,18 +15,12 @@ from __future__ import absolute_import
|
||||
|
||||
import base64
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
|
||||
from anchor import errors
|
||||
|
||||
|
||||
# RFC1034 allows a simple " " too, but it's not allowed in certificates, so it
|
||||
# will not match
|
||||
RE_DOMAIN_LABEL = re.compile("^[a-z](?:[-a-z0-9]*[a-z0-9])?$", re.IGNORECASE)
|
||||
|
||||
|
||||
def verify_domain(domain, allow_wildcards=False):
|
||||
def verify_domain(domain, label_re_comp, allow_wildcards=False):
|
||||
labels = domain.split('.')
|
||||
if labels[-1] == "":
|
||||
# single trailing . is ok, ignore
|
||||
@ -44,7 +38,7 @@ def verify_domain(domain, allow_wildcards=False):
|
||||
"domain <%s> has wildcard that's not in the "
|
||||
"left-most label (RFC6125/6.4.3)" % (domain,))
|
||||
else:
|
||||
if RE_DOMAIN_LABEL.match(label) is None:
|
||||
if label_re_comp.match(label) is None:
|
||||
raise ValueError(
|
||||
"domain <%s> contains invalid characters "
|
||||
"(RFC1034/3.5)" % (domain,))
|
||||
|
@ -28,12 +28,22 @@ from anchor.validators import errors
|
||||
from anchor.X509 import errors as x509_errors
|
||||
from anchor.X509 import extension
|
||||
|
||||
import re
|
||||
|
||||
def standards_compliance(csr=None, **kwargs):
|
||||
|
||||
# RFC1034 allows a simple " " too, but it's not allowed in certificates, so it
|
||||
# will not match
|
||||
#
|
||||
# This pattern is RFC1034 compatible otherwise, but since newer RFCs actually
|
||||
# allow any binary value as the domain label, some operators may want to relax
|
||||
# the pattern in the configuration, for example to allow leading digits or
|
||||
# hyphens.
|
||||
def standards_compliance(csr=None, label_re="^[a-z](?:[-a-z0-9]*[a-z0-9])?$",
|
||||
**kwargs):
|
||||
"""Collection of separate cases of standards validation."""
|
||||
_no_extension_duplicates(csr)
|
||||
_critical_flags(csr)
|
||||
_valid_domains(csr)
|
||||
_valid_domains(csr, label_re)
|
||||
_csr_signature(csr)
|
||||
# TODO(stan): validate srv/uri, distinct DNs, email format, identity keys
|
||||
|
||||
@ -67,7 +77,7 @@ def _critical_flags(csr):
|
||||
"(RFC5280/4.1.2.9)")
|
||||
|
||||
|
||||
def _valid_domains(csr):
|
||||
def _valid_domains(csr, label_re="^[a-z](?:[-a-z0-9]*[a-z0-9])?$"):
|
||||
"""Format of the domin names
|
||||
|
||||
See RFC5280 section 4.2.1.6 / RFC6125 / RFC1034
|
||||
@ -76,10 +86,12 @@ def _valid_domains(csr):
|
||||
if not sans:
|
||||
return
|
||||
|
||||
label_re_comp = re.compile(label_re, re.IGNORECASE)
|
||||
|
||||
ext = sans[0]
|
||||
for domain in ext.get_dns_ids():
|
||||
try:
|
||||
util.verify_domain(domain, allow_wildcards=True)
|
||||
util.verify_domain(domain, label_re_comp, allow_wildcards=True)
|
||||
except ValueError as e:
|
||||
raise errors.ValidationError(str(e))
|
||||
|
||||
|
@ -21,7 +21,9 @@
|
||||
"authentication": "method_1",
|
||||
"signing_ca": "local",
|
||||
"validators": {
|
||||
"standards_compliance": {},
|
||||
"standards_compliance": {
|
||||
"label_re": "^[a-z](?:[-a-z0-9]*[a-z0-9])?$"
|
||||
},
|
||||
"source_cidrs": {
|
||||
"cidrs": ["127.0.0.0/8"]
|
||||
}
|
||||
|
@ -21,6 +21,10 @@ The following validators are implemented at the moment:
|
||||
Any requests produced using standard tooling that fail this check should be
|
||||
reported as Anchor issues.
|
||||
|
||||
Parameters:
|
||||
|
||||
- ``label_re``: pattern for acceptable domain label format
|
||||
|
||||
``whitelist_names``
|
||||
Verifies: CSR. Parameters:
|
||||
|
||||
|
@ -146,11 +146,6 @@ class TestSubjectAltName(unittest.TestCase):
|
||||
self.ext.add_ip(self.ip)
|
||||
self.assertEqual([self.domain], self.ext.get_dns_ids())
|
||||
|
||||
def test_add_dns_id_validation(self):
|
||||
self.ext.add_dns_id("good.exapmle.com")
|
||||
with self.assertRaises(errors.X509Error):
|
||||
self.ext.add_dns_id("-blah")
|
||||
|
||||
def test_ips(self):
|
||||
self.ext.add_dns_id(self.domain)
|
||||
self.ext.add_ip(self.ip)
|
||||
|
@ -163,6 +163,11 @@ class TestValidDomains(unittest.TestCase):
|
||||
with self.assertRaises(errors.ValidationError):
|
||||
standards._valid_domains(csr)
|
||||
|
||||
def test_custom_re(self):
|
||||
csr = self._create_csr_with_domain_san('123.example.com.')
|
||||
with self.assertRaises(errors.ValidationError):
|
||||
standards._valid_domains(csr, "^\s\+$")
|
||||
|
||||
|
||||
class TestCsrSignature(tests.DefaultRequestMixin, unittest.TestCase):
|
||||
def test_csr_signature(self):
|
||||
|
Loading…
x
Reference in New Issue
Block a user