Remove bad ca_status validator. Always reject CA
Remove a validator which has been marked for an update for some time. CA certificate signing should not be handled by Anchor at all. Change-Id: Ib13a0ca3445956e35c23c559f59f37e6721c1a33 Closes-bug: 1508776
This commit is contained in:
parent
d5db378a6f
commit
c6cb4d9b3d
|
@ -17,14 +17,22 @@ import logging
|
|||
|
||||
from anchor import jsonloader
|
||||
from anchor.validators import errors
|
||||
from anchor.validators import internal
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# some validators will be always active because they enforce Anchor design
|
||||
# ideas rather than user configuration
|
||||
ENFORCED_VALIDATORS = [
|
||||
internal.ca_status
|
||||
]
|
||||
|
||||
def _run_validator(name, body, args):
|
||||
|
||||
def _run_validator(name, validator, body, args):
|
||||
"""Parse the validator tuple, call the validator, and return result.
|
||||
|
||||
:param name: the validator name
|
||||
:param validator: the validator callable
|
||||
:param body: validator body, directly from config
|
||||
:param args: additional arguments to pass to the validator function
|
||||
:return: True on success, else False
|
||||
|
@ -36,7 +44,6 @@ def _run_validator(name, body, args):
|
|||
# perform the actual check
|
||||
logger.debug("_run_validator: checking <%s> with rules: %s", name, body)
|
||||
try:
|
||||
validator = jsonloader.conf.get_validator(name)
|
||||
validator(**new_kwargs)
|
||||
logger.debug("_run_validator: success: <%s> ", name)
|
||||
return True # validator passed b/c no exceptions
|
||||
|
@ -66,7 +73,12 @@ def validate_csr(ra_name, auth_result, csr, request):
|
|||
|
||||
# It is ok if the config doesn't have any validators listed
|
||||
valid = {}
|
||||
for vname, validator in ra_conf['validators'].items():
|
||||
valid[vname] = _run_validator(vname, validator, args)
|
||||
for validator in ENFORCED_VALIDATORS:
|
||||
vname = validator.__name__
|
||||
valid[vname] = _run_validator(vname, validator, {}, args)
|
||||
|
||||
for vname, options in ra_conf['validators'].items():
|
||||
validator = jsonloader.conf.get_validator(vname)
|
||||
valid[vname] = _run_validator(vname, validator, options, args)
|
||||
|
||||
return valid
|
||||
|
|
|
@ -190,30 +190,6 @@ def ext_key_usage(csr=None, allowed_usage=None, **kwargs):
|
|||
% ', '.join(text_denied))
|
||||
|
||||
|
||||
def ca_status(csr=None, ca_requested=False, **kwargs):
|
||||
"""Ensure the request has/hasn't got the CA flag."""
|
||||
request_ca_flags = False
|
||||
for ext in (csr.get_extensions() or []):
|
||||
if isinstance(ext, extension.X509ExtensionBasicConstraints):
|
||||
if ext.get_ca():
|
||||
if not ca_requested:
|
||||
raise v_errors.ValidationError(
|
||||
"CA status requested, but not allowed")
|
||||
request_ca_flags = True
|
||||
elif isinstance(ext, extension.X509ExtensionKeyUsage):
|
||||
has_cert_sign = ext.get_usage('keyCertSign')
|
||||
has_crl_sign = ext.get_usage('cRLSign')
|
||||
if has_crl_sign or has_cert_sign:
|
||||
if not ca_requested:
|
||||
raise v_errors.ValidationError(
|
||||
"Key usage doesn't match requested CA status "
|
||||
"(keyCertSign/cRLSign: %s/%s)"
|
||||
% (has_cert_sign, has_crl_sign))
|
||||
request_ca_flags = True
|
||||
if ca_requested and not request_ca_flags:
|
||||
raise v_errors.ValidationError("CA flags required")
|
||||
|
||||
|
||||
def source_cidrs(request=None, cidrs=None, **kwargs):
|
||||
"""Ensure that the request comes from a known source."""
|
||||
for cidr in cidrs:
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Anchor internally used validators. They should not be exposed to the
|
||||
users.
|
||||
"""
|
||||
|
||||
from anchor.validators import errors as v_errors
|
||||
from anchor.X509 import extension
|
||||
|
||||
|
||||
def ca_status(csr=None, **kwargs):
|
||||
"""Ensure the request hasn't got the CA or cert signing flag.
|
||||
|
||||
This validation applies both to the BasicConstraints extension and to the
|
||||
KeyUsage extension.
|
||||
"""
|
||||
basic_constraint = csr.get_extensions(
|
||||
extension.X509ExtensionBasicConstraints)
|
||||
if basic_constraint:
|
||||
if basic_constraint[0].get_ca():
|
||||
raise v_errors.ValidationError(
|
||||
"Request is for a CA certificate")
|
||||
|
||||
key_usage = csr.get_extensions(extension.X509ExtensionKeyUsage)
|
||||
if key_usage:
|
||||
if key_usage[0].get_usage('keyCertSign'):
|
||||
raise v_errors.ValidationError(
|
||||
"Request contains certificates signing usage flag")
|
||||
if key_usage[0].get_usage('cRLSign'):
|
||||
raise v_errors.ValidationError(
|
||||
"Request contains CRL signing usage flag")
|
|
@ -30,3 +30,11 @@ they're not critical), or will prevent signing (if they are):
|
|||
|
||||
Other extensions will be added to the implementation when they're needed for
|
||||
validation / fixups.
|
||||
|
||||
Extension limitations
|
||||
=====================
|
||||
Due to how Anchor relies on short-term certificates, issuing a CA certificate
|
||||
from Anchor doesn't really make sense. Certificates which do have a CA flag set
|
||||
will be rejected unconditionally.
|
||||
|
||||
Key usage related to CA status will be treated in a similar way.
|
||||
|
|
|
@ -107,14 +107,6 @@ The following validators are implemented at the moment:
|
|||
|
||||
or text representation of custom OIDs.
|
||||
|
||||
``ca_status``
|
||||
Verifies: CSR. Parameters: ``ca_requested``.
|
||||
|
||||
Ensures the request does/doesn't require the CA flag.
|
||||
|
||||
This is not a well designed validator and may not be safe to use! A better
|
||||
version is on the TODO list.
|
||||
|
||||
``source_cidrs``
|
||||
Verifies: CSR. Parameters: ``cidrs``.
|
||||
|
||||
|
|
|
@ -41,7 +41,6 @@ anchor.validators =
|
|||
server_group = anchor.validators.custom:server_group
|
||||
extensions = anchor.validators.custom:extensions
|
||||
key_usage = anchor.validators.custom:key_usage
|
||||
ca_status = anchor.validators.custom:ca_status
|
||||
source_cidrs = anchor.validators.custom:source_cidrs
|
||||
standards_compliance = anchor.validators.standards:standards_compliance
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ from pyasn1_modules import rfc2459
|
|||
|
||||
from anchor.validators import custom
|
||||
from anchor.validators import errors
|
||||
from anchor.validators import internal
|
||||
from anchor.validators import utils
|
||||
from anchor.X509 import extension as x509_ext
|
||||
from anchor.X509 import name as x509_name
|
||||
|
@ -410,33 +411,13 @@ class TestValidators(unittest.TestCase):
|
|||
self.assertEqual("Found some prohibited key usages: "
|
||||
"clientAuth", str(e.exception))
|
||||
|
||||
def test_ca_status_good1(self):
|
||||
csr = x509_csr.X509Csr()
|
||||
ext = x509_ext.X509ExtensionBasicConstraints()
|
||||
ext.set_ca(True)
|
||||
csr.add_extension(ext)
|
||||
|
||||
self.assertEqual(
|
||||
None,
|
||||
custom.ca_status(
|
||||
csr=csr,
|
||||
ca_requested=True
|
||||
)
|
||||
)
|
||||
|
||||
def test_ca_status_good2(self):
|
||||
def test_ca_status_good(self):
|
||||
csr = x509_csr.X509Csr()
|
||||
ext = x509_ext.X509ExtensionBasicConstraints()
|
||||
ext.set_ca(False)
|
||||
csr.add_extension(ext)
|
||||
|
||||
self.assertEqual(
|
||||
None,
|
||||
custom.ca_status(
|
||||
csr=csr,
|
||||
ca_requested=False
|
||||
)
|
||||
)
|
||||
self.assertEqual(None, internal.ca_status(csr=csr))
|
||||
|
||||
def test_ca_status_forbidden(self):
|
||||
csr = x509_csr.X509Csr()
|
||||
|
@ -445,23 +426,8 @@ class TestValidators(unittest.TestCase):
|
|||
csr.add_extension(ext)
|
||||
|
||||
with self.assertRaises(errors.ValidationError) as e:
|
||||
custom.ca_status(
|
||||
csr=csr,
|
||||
ca_requested=False)
|
||||
self.assertEqual("CA status requested, but not allowed",
|
||||
str(e.exception))
|
||||
|
||||
def test_ca_status_bad(self):
|
||||
csr = x509_csr.X509Csr()
|
||||
ext = x509_ext.X509ExtensionBasicConstraints()
|
||||
ext.set_ca(False)
|
||||
csr.add_extension(ext)
|
||||
|
||||
with self.assertRaises(errors.ValidationError) as e:
|
||||
custom.ca_status(
|
||||
csr=csr,
|
||||
ca_requested=True)
|
||||
self.assertEqual("CA flags required",
|
||||
internal.ca_status(csr=csr)
|
||||
self.assertEqual("Request is for a CA certificate",
|
||||
str(e.exception))
|
||||
|
||||
def test_ca_status_pathlen(self):
|
||||
|
@ -470,13 +436,7 @@ class TestValidators(unittest.TestCase):
|
|||
ext.set_path_len_constraint(1)
|
||||
csr.add_extension(ext)
|
||||
|
||||
self.assertEqual(
|
||||
None,
|
||||
custom.ca_status(
|
||||
csr=csr,
|
||||
ca_requested=False
|
||||
)
|
||||
)
|
||||
self.assertEqual(None, internal.ca_status(csr=csr))
|
||||
|
||||
def test_ca_status_key_usage_bad1(self):
|
||||
csr = x509_csr.X509Csr()
|
||||
|
@ -485,25 +445,9 @@ class TestValidators(unittest.TestCase):
|
|||
csr.add_extension(ext)
|
||||
|
||||
with self.assertRaises(errors.ValidationError) as e:
|
||||
custom.ca_status(
|
||||
csr=csr,
|
||||
ca_requested=False)
|
||||
self.assertEqual("Key usage doesn't match requested CA status "
|
||||
"(keyCertSign/cRLSign: True/False)", str(e.exception))
|
||||
|
||||
def test_ca_status_key_usage_good1(self):
|
||||
csr = x509_csr.X509Csr()
|
||||
ext = x509_ext.X509ExtensionKeyUsage()
|
||||
ext.set_usage('keyCertSign', True)
|
||||
csr.add_extension(ext)
|
||||
|
||||
self.assertEqual(
|
||||
None,
|
||||
custom.ca_status(
|
||||
csr=csr,
|
||||
ca_requested=True
|
||||
)
|
||||
)
|
||||
internal.ca_status(csr=csr)
|
||||
self.assertEqual("Request contains certificates signing usage flag",
|
||||
str(e.exception))
|
||||
|
||||
def test_ca_status_key_usage_bad2(self):
|
||||
csr = x509_csr.X509Csr()
|
||||
|
@ -512,25 +456,9 @@ class TestValidators(unittest.TestCase):
|
|||
csr.add_extension(ext)
|
||||
|
||||
with self.assertRaises(errors.ValidationError) as e:
|
||||
custom.ca_status(
|
||||
csr=csr,
|
||||
ca_requested=False)
|
||||
self.assertEqual("Key usage doesn't match requested CA status "
|
||||
"(keyCertSign/cRLSign: False/True)", str(e.exception))
|
||||
|
||||
def test_ca_status_key_usage_good2(self):
|
||||
csr = x509_csr.X509Csr()
|
||||
ext = x509_ext.X509ExtensionKeyUsage()
|
||||
ext.set_usage('cRLSign', True)
|
||||
csr.add_extension(ext)
|
||||
|
||||
self.assertEqual(
|
||||
None,
|
||||
custom.ca_status(
|
||||
csr=csr,
|
||||
ca_requested=True
|
||||
)
|
||||
)
|
||||
internal.ca_status(csr=csr)
|
||||
self.assertEqual("Request contains CRL signing usage flag",
|
||||
str(e.exception))
|
||||
|
||||
def test_source_cidrs_good(self):
|
||||
request = mock.Mock(client_addr='127.0.0.1')
|
||||
|
|
Loading…
Reference in New Issue