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:
Stanisław Pitucha 2015-10-22 16:36:11 +11:00
parent d5db378a6f
commit c6cb4d9b3d
7 changed files with 78 additions and 121 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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