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:
Stanisław Pitucha 2016-06-16 16:15:59 +10:00
parent 6fdb9be69e
commit 2257b8bbed
7 changed files with 30 additions and 24 deletions

View File

@ -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

View File

@ -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,))

View File

@ -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))

View File

@ -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"]
}

View File

@ -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:

View File

@ -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)

View File

@ -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):