move MSCSTemplate classes to ipalib

As we expand the integration tests for external CA functionality, it
is helpful (and avoids duplication) to use the MSCSTemplate*
classes.  These currently live in ipaserver.install.cainstance, but
ipatests is no longer permitted to import from ipaserver (see commit
81714976e5e13131654c78eb734746a20237c933).  So move these classes to
ipalib.

Part of: https://pagure.io/freeipa/issue/7548

Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
This commit is contained in:
Fraser Tweedale
2019-07-11 15:17:04 +10:00
committed by Alexander Bokovoy
parent 95c2b34c4b
commit 130e1dc343
8 changed files with 307 additions and 324 deletions

View File

@@ -28,7 +28,7 @@ from ipaplatform import services
from ipaplatform.paths import paths
from ipaserver.install import installutils, certs
from ipaserver.install.replication import replica_conn_check
from ipalib import api, errors
from ipalib import api, errors, x509
from ipapython.dn import DN
from . import conncheck, dogtag, cainstance
@@ -216,8 +216,7 @@ def install_check(standalone, replica_config, options):
paths.ROOT_IPA_CSR)
if not options.external_ca_type:
options.external_ca_type = \
cainstance.ExternalCAType.GENERIC.value
options.external_ca_type = x509.ExternalCAType.GENERIC.value
if options.external_ca_profile is not None:
# check that profile is valid for the external ca type
@@ -478,13 +477,11 @@ class CAInstallInterface(dogtag.DogtagInstallInterface,
external_ca = master_install_only(external_ca)
external_ca_type = knob(
cainstance.ExternalCAType, None,
description="Type of the external CA",
)
x509.ExternalCAType, None, description="Type of the external CA")
external_ca_type = master_install_only(external_ca_type)
external_ca_profile = knob(
type=cainstance.ExternalCAProfile,
type=x509.ExternalCAProfile,
default=None,
description=(
"Specify the certificate profile/template to use at the "

View File

@@ -26,7 +26,6 @@ import binascii
import logging
import dbus
import enum
import ldap
import os
import pwd
@@ -39,10 +38,6 @@ import time
import tempfile
from configparser import RawConfigParser
from pyasn1.codec.der import encoder
from pyasn1.type import char, univ, namedtype
import pyasn1.error
from ipalib import api
from ipalib import x509
from ipalib import errors
@@ -80,11 +75,6 @@ ADMIN_GROUPS = [
]
class ExternalCAType(enum.Enum):
GENERIC = 'generic'
MS_CS = 'ms-cs'
def check_ports():
"""Check that dogtag ports (8080, 8443) are available.
@@ -367,7 +357,7 @@ class CAInstance(DogtagInstance):
if ca_type is not None:
self.ca_type = ca_type
else:
self.ca_type = ExternalCAType.GENERIC.value
self.ca_type = x509.ExternalCAType.GENERIC.value
self.external_ca_profile = external_ca_profile
self.no_db_setup = promote
@@ -537,12 +527,12 @@ class CAInstance(DogtagInstance):
pki_ca_signing_csr_path=self.csr_file,
)
if self.ca_type == ExternalCAType.MS_CS.value:
if self.ca_type == x509.ExternalCAType.MS_CS.value:
# Include MS template name extension in the CSR
template = self.external_ca_profile
if template is None:
# default template name
template = MSCSTemplateV1(u"SubCA")
template = x509.MSCSTemplateV1(u"SubCA")
ext_data = binascii.hexlify(template.get_ext_data())
cfg.update(
@@ -2081,170 +2071,6 @@ def update_ipa_conf():
parser.write(f)
class ExternalCAProfile:
"""
An external CA profile configuration. Currently the only
subclasses are for Microsoft CAs, for providing data in the
"Certificate Template" extension.
Constructing this class will actually return an instance of a
subclass.
Subclasses MUST set ``valid_for``.
"""
def __init__(self, s=None):
self.unparsed_input = s
# Which external CA types is the data valid for?
# A set of VALUES of the ExternalCAType enum.
valid_for = set()
def __new__(cls, s=None):
"""Construct the ExternalCAProfile value.
Return an instance of a subclass determined by
the format of the argument.
"""
# we are directly constructing a subclass; instantiate
# it and be done
if cls is not ExternalCAProfile:
return super(ExternalCAProfile, cls).__new__(cls)
# construction via the base class; therefore the string
# argument is required, and is used to determine which
# subclass to construct
if s is None:
raise ValueError('string argument is required')
parts = s.split(':')
try:
# Is the first part on OID?
_oid = univ.ObjectIdentifier(parts[0])
# It is; construct a V2 template
# pylint: disable=too-many-function-args
return MSCSTemplateV2.__new__(MSCSTemplateV2, s)
except pyasn1.error.PyAsn1Error:
# It is not an OID; treat as a template name
# pylint: disable=too-many-function-args
return MSCSTemplateV1.__new__(MSCSTemplateV1, s)
def __getstate__(self):
return self.unparsed_input
def __setstate__(self, state):
# explicitly call __init__ method to initialise object
self.__init__(state)
class MSCSTemplate(ExternalCAProfile):
"""
An Microsoft AD-CS Template specifier.
Subclasses MUST set ext_oid.
Subclass constructors MUST set asn1obj.
"""
valid_for = set([ExternalCAType.MS_CS.value])
ext_oid = None # extension OID, as a Python str
asn1obj = None # unencoded extension data
def get_ext_data(self):
"""Return DER-encoded extension data."""
return encoder.encode(self.asn1obj)
class MSCSTemplateV1(MSCSTemplate):
"""
A v1 template specifier, per
https://msdn.microsoft.com/en-us/library/cc250011.aspx.
::
CertificateTemplateName ::= SEQUENCE {
Name UTF8String
}
But note that a bare BMPString is used in practice.
"""
ext_oid = "1.3.6.1.4.1.311.20.2"
def __init__(self, s):
super(MSCSTemplateV1, self).__init__(s)
parts = s.split(':')
if len(parts) > 1:
raise ValueError(
"Cannot specify certificate template version when using name.")
self.asn1obj = char.BMPString(str(parts[0]))
class MSCSTemplateV2(MSCSTemplate):
"""
A v2 template specifier, per
https://msdn.microsoft.com/en-us/library/windows/desktop/aa378274(v=vs.85).aspx
::
CertificateTemplate ::= SEQUENCE {
templateID EncodedObjectID,
templateMajorVersion TemplateVersion,
templateMinorVersion TemplateVersion OPTIONAL
}
TemplateVersion ::= INTEGER (0..4294967295)
"""
ext_oid = "1.3.6.1.4.1.311.21.7"
@staticmethod
def check_version_in_range(desc, n):
if n < 0 or n >= 2**32:
raise ValueError(
"Template {} version must be in range 0..4294967295"
.format(desc))
def __init__(self, s):
super(MSCSTemplateV2, self).__init__(s)
parts = s.split(':')
obj = CertificateTemplateV2()
if len(parts) < 2 or len(parts) > 3:
raise ValueError(
"Incorrect template specification; required format is: "
"<oid>:<majorVersion>[:<minorVersion>]")
try:
obj['templateID'] = univ.ObjectIdentifier(parts[0])
major = int(parts[1])
self.check_version_in_range("major", major)
obj['templateMajorVersion'] = major
if len(parts) > 2:
minor = int(parts[2])
self.check_version_in_range("minor", minor)
obj['templateMinorVersion'] = int(parts[2])
except pyasn1.error.PyAsn1Error:
raise ValueError("Could not parse certificate template specifier.")
self.asn1obj = obj
class CertificateTemplateV2(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.NamedType('templateID', univ.ObjectIdentifier()),
namedtype.NamedType('templateMajorVersion', univ.Integer()),
namedtype.OptionalNamedType('templateMinorVersion', univ.Integer())
)
if __name__ == "__main__":
standard_logging_setup("install.log")
ds = dsinstance.DsInstance()

View File

@@ -65,7 +65,7 @@ class CACertManage(admintool.AdminTool):
"--external-ca", dest='self_signed',
action='store_false',
help="Sign the renewed certificate by external CA")
ext_cas = tuple(x.value for x in cainstance.ExternalCAType)
ext_cas = tuple(x.value for x in x509.ExternalCAType)
renew_group.add_option(
"--external-ca-type", dest="external_ca_type",
type="choice", choices=ext_cas,
@@ -73,7 +73,7 @@ class CACertManage(admintool.AdminTool):
help="Type of the external CA. Default: generic")
renew_group.add_option(
"--external-ca-profile", dest="external_ca_profile",
type='constructor', constructor=cainstance.ExternalCAProfile,
type='constructor', constructor=x509.ExternalCAProfile,
default=None, metavar="PROFILE-SPEC",
help="Specify the certificate profile/template to use "
"at the external CA")
@@ -224,11 +224,11 @@ class CACertManage(admintool.AdminTool):
options = self.options
if not options.external_ca_type:
options.external_ca_type = cainstance.ExternalCAType.GENERIC.value
options.external_ca_type = x509.ExternalCAType.GENERIC.value
if options.external_ca_type == cainstance.ExternalCAType.MS_CS.value \
if options.external_ca_type == x509.ExternalCAType.MS_CS.value \
and options.external_ca_profile is None:
options.external_ca_profile = cainstance.MSCSTemplateV1(u"SubCA")
options.external_ca_profile = x509.MSCSTemplateV1(u"SubCA")
if options.external_ca_profile is not None:
# check that profile is valid for the external ca type
@@ -352,11 +352,11 @@ class CACertManage(admintool.AdminTool):
timeout = api.env.startup_timeout + 60
cm_profile = None
if isinstance(profile, cainstance.MSCSTemplateV1):
if isinstance(profile, x509.MSCSTemplateV1):
cm_profile = profile.unparsed_input
cm_template = None
if isinstance(profile, cainstance.MSCSTemplateV2):
if isinstance(profile, x509.MSCSTemplateV2):
cm_template = profile.unparsed_input
logger.debug("resubmitting certmonger request '%s'", self.request_id)