[Freeipa-devel] [PATCH] 0097 Add options to write lightweight CA cert or chain to file

Fraser Tweedale ftweedal at redhat.com
Mon Aug 8 04:34:24 UTC 2016


Please review the attached patch with adds --certificate-out and
--certificate-chain-out options to `ca-show' command.

Note that --certificate-chain-out currently writes a bogus file due
to a bug in Dogtag that will be fixed in this week's build.

https://fedorahosted.org/freeipa/ticket/6178

Thanks,
Fraser
-------------- next part --------------
From 6d3a153a954ab09022af6073ae9ea68668716618 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Mon, 8 Aug 2016 14:27:20 +1000
Subject: [PATCH] Add options to write lightweight CA cert or chain to file

Administrators need a way to retrieve the certificate or certificate
chain of an IPA-managed lightweight CA.  Add --certificate-out and
--certificate-chain-out options to the `ca-show` command.

Fixes: https://fedorahosted.org/freeipa/ticket/6178
---
 API.txt                     |  4 +++-
 VERSION                     |  4 ++--
 ipaclient/plugins/ca.py     | 40 ++++++++++++++++++++++++++++++++++++++++
 ipaserver/plugins/ca.py     | 23 +++++++++++++++++++++--
 ipaserver/plugins/dogtag.py | 12 ++++++++++++
 5 files changed, 78 insertions(+), 5 deletions(-)
 create mode 100644 ipaclient/plugins/ca.py

diff --git a/API.txt b/API.txt
index 535d8ec9a4990395207e2455a09a8c1bdef5529a..6ed8c5348876aa6ce1ab5d11e14dbcb1e7cee768 100644
--- a/API.txt
+++ b/API.txt
@@ -505,9 +505,11 @@ output: Entry('result')
 output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
 output: PrimaryKey('value')
 command: ca_show/1
-args: 1,4,3
+args: 1,6,3
 arg: Str('cn', cli_name='name')
 option: Flag('all', autofill=True, cli_name='all', default=False)
+option: Str('certificate_chain_out?')
+option: Str('certificate_out?')
 option: Flag('raw', autofill=True, cli_name='raw', default=False)
 option: Flag('rights', autofill=True, default=False)
 option: Str('version?')
diff --git a/VERSION b/VERSION
index ca489965050f32d2d8987dfd251ec2b2a0ba1768..3cdb27b806013be2cb0b8b2132c99302865e6358 100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=211
-# Last change: mbabinsk: allow 'value' output param in commands without primary key
+IPA_API_VERSION_MINOR=212
+# Last change: ftweedal: ca: add options to write cert or cert chain to file
diff --git a/ipaclient/plugins/ca.py b/ipaclient/plugins/ca.py
new file mode 100644
index 0000000000000000000000000000000000000000..1f4a7c6074b5eb139e01ba2963bf46399ce6d645
--- /dev/null
+++ b/ipaclient/plugins/ca.py
@@ -0,0 +1,40 @@
+#
+# Copyright (C) 2016  FreeIPA Contributors see COPYING for license
+#
+
+from ipaclient.frontend import MethodOverride
+from ipalib import util
+from ipalib.plugable import Registry
+from ipalib.text import _
+
+register = Registry()
+
+
+ at register(override=True, no_fail=True)
+class ca_show(MethodOverride):
+    def forward(self, *keys, **options):
+        if 'certificate_out' in options:
+            util.check_writable_file(options['certificate_out'])
+        if 'certificate_chain_out' in options:
+            util.check_writable_file(options['certificate_chain_out'])
+
+        result = super(ca_show, self).forward(*keys, **options)
+        summaries = []
+        if 'certificate_out' in options and 'certificate' in result['result']:
+            with open(options['certificate_out'], 'wb') as f:
+                f.write(result['result'].pop('certificate'))
+            summaries.append (
+                _("Certificate written to file '%(file)s'")
+                % dict(file=options['certificate_out'])
+            )
+        if 'certificate_chain_out' in options and 'chain' in result['result']:
+            with open(options['certificate_chain_out'], 'wb') as f:
+                f.write(result['result'].pop('chain'))
+            summaries.append (
+                _("Certificate chain written to file '%(file)s'")
+                % dict(file=options['certificate_chain_out'])
+            )
+        if len(summaries) > 0:
+            result['summary'] = '\n'.join(summaries)
+
+        return result
diff --git a/ipaserver/plugins/ca.py b/ipaserver/plugins/ca.py
index 966ae2b1bdb4bb0207dfa58f0e9c951bc930f766..70aaca19dbece452c8e679a58ce3754ea75666c5 100644
--- a/ipaserver/plugins/ca.py
+++ b/ipaserver/plugins/ca.py
@@ -140,9 +140,28 @@ class ca_find(LDAPSearch):
 class ca_show(LDAPRetrieve):
     __doc__ = _("Display the properties of a CA.")
 
-    def execute(self, *args, **kwargs):
+    takes_options = LDAPRetrieve.takes_options + (
+        Str('certificate_out?',
+            doc=_('Write certificate to file'),
+        ),
+        Str('certificate_chain_out?',
+            doc=_('Write PKCS #7 certificate chain to file'),
+        ),
+    )
+
+    def execute(self, *keys, **options):
         ca_enabled_check()
-        return super(ca_show, self).execute(*args, **kwargs)
+        result = super(ca_show, self).execute(*keys, **options)
+
+        ca_id = result['result']['ipacaid'][0]
+        if 'certificate_out' in options:
+            with self.api.Backend.ra_lightweight_ca as ca_api:
+                result['result']['certificate'] = ca_api.read_ca_cert(ca_id)
+        if 'certificate_chain_out' in options:
+            with self.api.Backend.ra_lightweight_ca as ca_api:
+                result['result']['chain'] = ca_api.read_ca_chain(ca_id)
+
+        return result
 
 
 @register()
diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py
index aef1e888eb1b6c273c1fd12cbf4912407f8f8132..ea78f4ee93d3bd16a2088cf82618e72a9dcf9268 100644
--- a/ipaserver/plugins/dogtag.py
+++ b/ipaserver/plugins/dogtag.py
@@ -2205,6 +2205,18 @@ class ra_lightweight_ca(RestClient):
         except:
             raise errors.RemoteRetrieveError(reason=_("Response from CA was not valid JSON"))
 
+    def read_ca_cert(self, ca_id):
+        status, resp_headers, resp_body = self._ssldo(
+            'GET', '{}/cert'.format(ca_id),
+            headers={'Accept': 'application/x-pem-file'})
+        return resp_body
+
+    def read_ca_chain(self, ca_id):
+        status, resp_headers, resp_body = self._ssldo(
+            'GET', '{}/chain'.format(ca_id),
+            headers={'Accept': 'application/x-pem-file'})
+        return resp_body
+
     def disable_ca(self, ca_id):
         self._ssldo(
             'POST', ca_id + '/disable',
-- 
2.5.5



More information about the Freeipa-devel mailing list