summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Cholasta <jcholast@redhat.com>2017-03-10 09:22:42 +0000
committerDavid Kupka <dkupka@redhat.com>2017-03-14 12:58:45 +0100
commit8ed891cb619abd2efd428f767edf760ebf5eec5d (patch)
tree4e15ed642fa731001cdf2d17bc0ff95245596855
parentc60d9c9744b1f8a7b55bcdda65cce8bb36700bf6 (diff)
downloadfreeipa-8ed891cb619abd2efd428f767edf760ebf5eec5d.tar.gz
freeipa-8ed891cb619abd2efd428f767edf760ebf5eec5d.tar.xz
freeipa-8ed891cb619abd2efd428f767edf760ebf5eec5d.zip
cert: include certificate chain in cert command output
Include the full certificate chain in the output of cert-request, cert-show and cert-find if --chain or --all is specified. If output file is specified in the CLI together with --chain, the full certificate chain is written to the file. https://pagure.io/freeipa/issue/6547 Reviewed-By: David Kupka <dkupka@redhat.com>
-rw-r--r--API.txt6
-rw-r--r--VERSION.m44
-rw-r--r--ipaclient/plugins/cert.py5
-rw-r--r--ipaserver/plugins/cert.py53
4 files changed, 56 insertions, 12 deletions
diff --git a/API.txt b/API.txt
index 90cda748a..2d6b401be 100644
--- a/API.txt
+++ b/API.txt
@@ -782,11 +782,12 @@ option: Str('cacn?', autofill=True, cli_name='ca', default=u'ipa')
option: Str('version?')
output: Output('result')
command: cert_request/1
-args: 1,8,3
+args: 1,9,3
arg: Str('csr', cli_name='csr_file')
option: Flag('add', autofill=True, default=False)
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('cacn?', autofill=True, cli_name='ca', default=u'ipa')
+option: Flag('chain', autofill=True, default=False)
option: Principal('principal')
option: Str('profile_id?')
option: Flag('raw', autofill=True, cli_name='raw', default=False)
@@ -803,10 +804,11 @@ option: Int('revocation_reason', autofill=True, default=0)
option: Str('version?')
output: Output('result')
command: cert_show/1
-args: 1,6,3
+args: 1,7,3
arg: Int('serial_number')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('cacn?', autofill=True, cli_name='ca', default=u'ipa')
+option: Flag('chain', autofill=True, default=False)
option: Flag('no_members', autofill=True, default=False)
option: Str('out?')
option: Flag('raw', autofill=True, cli_name='raw', default=False)
diff --git a/VERSION.m4 b/VERSION.m4
index f9435664d..246d6bb59 100644
--- a/VERSION.m4
+++ b/VERSION.m4
@@ -73,8 +73,8 @@ define(IPA_DATA_VERSION, 20100614120000)
# #
########################################################
define(IPA_API_VERSION_MAJOR, 2)
-define(IPA_API_VERSION_MINOR, 220)
-# Last change: Add whoami command
+define(IPA_API_VERSION_MINOR, 221)
+# Last change: cert: include certificate chain in cert command output
########################################################
diff --git a/ipaclient/plugins/cert.py b/ipaclient/plugins/cert.py
index 62171e92f..9ec6970b1 100644
--- a/ipaclient/plugins/cert.py
+++ b/ipaclient/plugins/cert.py
@@ -57,7 +57,10 @@ class CertRetrieveOverride(MethodOverride):
result = super(CertRetrieveOverride, self).forward(*args, **options)
if certificate_out is not None:
- certs = [result['result']['certificate']]
+ if options.get('chain', False):
+ certs = result['result']['certificate_chain']
+ else:
+ certs = [result['result']['certificate']]
certs = (x509.normalize_certificate(cert) for cert in certs)
certs = (x509.make_pem(base64.b64encode(cert)) for cert in certs)
with open(certificate_out, 'w') as f:
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
index fb16f5b97..8b9b86369 100644
--- a/ipaserver/plugins/cert.py
+++ b/ipaserver/plugins/cert.py
@@ -267,6 +267,12 @@ class BaseCertObject(Object):
normalizer=x509.normalize_certificate,
flags={'no_create', 'no_update', 'no_search'},
),
+ Bytes(
+ 'certificate_chain*',
+ label=_("Certificate chain"),
+ doc=_("X.509 certificate chain"),
+ flags={'no_create', 'no_update', 'no_search'},
+ ),
DNParam(
'subject',
label=_('Subject'),
@@ -495,6 +501,13 @@ class certreq(BaseCertObject):
)
+_chain_flag = Flag(
+ 'chain',
+ default=False,
+ doc=_('Include certificate chain in output'),
+)
+
+
@register()
class cert_request(Create, BaseCertMethod, VirtualCommand):
__doc__ = _('Submit a certificate signing request.')
@@ -526,6 +539,7 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
"automatically add the principal if it doesn't exist "
"(service principals only)"),
),
+ _chain_flag,
)
def get_args(self):
@@ -535,7 +549,7 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
continue
yield arg
- def execute(self, csr, all=False, raw=False, **kw):
+ def execute(self, csr, all=False, raw=False, chain=False, **kw):
ca_enabled_check(self.api)
ldap = self.api.Backend.ldap2
@@ -549,7 +563,7 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
# referencing nonexistant CA) and look up authority ID.
#
ca = kw['cacn']
- ca_obj = api.Command.ca_show(ca)['result']
+ ca_obj = api.Command.ca_show(ca, all=all, chain=chain)['result']
ca_id = ca_obj['ipacaid'][0]
"""
@@ -823,6 +837,11 @@ class cert_request(Create, BaseCertMethod, VirtualCommand):
self.log.error("Profiles used to store cert should't be "
"used for krbtgt certificates")
+ if 'certificate_chain' in ca_obj:
+ cert = x509.load_certificate(result['certificate'])
+ cert = cert.public_bytes(serialization.Encoding.DER)
+ result['certificate_chain'] = [cert] + ca_obj['certificate_chain']
+
return dict(
result=result,
value=pkey_to_value(int(result['request_id']), kw),
@@ -999,12 +1018,13 @@ class cert_show(Retrieve, CertMethod, VirtualCommand):
doc=_('File to store the certificate in.'),
exclude='webui',
),
+ _chain_flag,
)
operation="retrieve certificate"
def execute(self, serial_number, all=False, raw=False, no_members=False,
- **options):
+ chain=False, **options):
ca_enabled_check(self.api)
# Dogtag lightweight CAs have shared serial number domain, so
@@ -1020,7 +1040,11 @@ class cert_show(Retrieve, CertMethod, VirtualCommand):
if not bind_principal_can_manage_cert(cert):
raise acierr # pylint: disable=E0702
- ca_obj = api.Command.ca_show(options['cacn'])['result']
+ ca_obj = api.Command.ca_show(
+ options['cacn'],
+ all=all,
+ chain=chain,
+ )['result']
if DN(cert.issuer) != DN(ca_obj['ipacasubjectdn'][0]):
# DN of cert differs from what we requested
raise errors.NotFound(
@@ -1028,10 +1052,11 @@ class cert_show(Retrieve, CertMethod, VirtualCommand):
"issued by CA '%(ca)s' not found")
% dict(serial=serial_number, ca=options['cacn']))
+ der_cert = base64.b64decode(result['certificate'])
+
if all or not no_members:
ldap = self.api.Backend.ldap2
- filter = ldap.make_filter_from_attr(
- 'usercertificate', base64.b64decode(result['certificate']))
+ filter = ldap.make_filter_from_attr('usercertificate', der_cert)
try:
entries = ldap.get_entries(base_dn=self.api.env.basedn,
filter=filter,
@@ -1048,6 +1073,10 @@ class cert_show(Retrieve, CertMethod, VirtualCommand):
self.obj._fill_owners(result)
result['cacn'] = ca_obj['cn'][0]
+ if 'certificate_chain' in ca_obj:
+ result['certificate_chain'] = (
+ [der_cert] + ca_obj['certificate_chain'])
+
return dict(result=result, value=pkey_to_value(serial_number, options))
@@ -1319,7 +1348,11 @@ class cert_find(Search, CertMethod):
raise
return result, False, complete
- ca_objs = self.api.Command.ca_find(timelimit=0, sizelimit=0)['result']
+ ca_objs = self.api.Command.ca_find(
+ all=all,
+ timelimit=0,
+ sizelimit=0,
+ )['result']
ca_objs = {DN(ca['ipacasubjectdn'][0]): ca for ca in ca_objs}
ra = self.api.Backend.ra
@@ -1354,6 +1387,12 @@ class cert_find(Search, CertMethod):
obj['certificate'].replace('\r\n', ''))
self.obj._parse(obj)
+ if 'certificate_chain' in ca_obj:
+ cert = x509.load_certificate(obj['certificate'])
+ cert_der = cert.public_bytes(serialization.Encoding.DER)
+ obj['certificate_chain'] = (
+ [cert_der] + ca_obj['certificate_chain'])
+
obj['cacn'] = ca_obj['cn'][0]
result[issuer, serial_number] = obj