>From 3e05127a0a6234b76c19c0f4cc193d067b590242 Mon Sep 17 00:00:00 2001
From: "Endi S. Dewata" <edewata@redhat.com>
Date: Wed, 11 May 2016 19:33:51 +0200
Subject: [PATCH] Fixed missing CSR extensions for external CA case.

The deployment tool has been modified to generate CSR with basic
constraints and key usage extensions for the externally-signed CA
signing certificate.

https://fedorahosted.org/pki/ticket/2312
---
 base/common/python/pki/nssdb.py                    | 50 +++++++++++++++++++++-
 .../server/deployment/scriptlets/configuration.py  | 23 +++++++++-
 2 files changed, 70 insertions(+), 3 deletions(-)

diff --git a/base/common/python/pki/nssdb.py b/base/common/python/pki/nssdb.py
index 30b1d479375af3cb5705411d9af6cc24857d18f3..7908461b1b0735ddd8f678f6b89efef0b25127bc 100644
--- a/base/common/python/pki/nssdb.py
+++ b/base/common/python/pki/nssdb.py
@@ -169,7 +169,10 @@ class NSSDatabase(object):
 
     def create_request(self, subject_dn, request_file, noise_file=None,
                        key_type=None, key_size=None, curve=None,
-                       hash_alg=None):
+                       hash_alg=None,
+                       basic_constraints_ext=None,
+                       key_usage_ext=None):
+
         tmpdir = tempfile.mkdtemp()
 
         try:
@@ -185,6 +188,8 @@ class NSSDatabase(object):
 
             binary_request_file = os.path.join(tmpdir, 'request.bin')
 
+            keystroke = ''
+
             cmd = [
                 'certutil',
                 '-R',
@@ -213,8 +218,49 @@ class NSSDatabase(object):
             if hash_alg:
                 cmd.extend(['-Z', hash_alg])
 
+            if key_usage_ext:
+
+                cmd.extend(['--keyUsage'])
+
+                usages = []
+                for usage in key_usage_ext:
+                    if key_usage_ext[usage]:
+                        usages.append(usage)
+
+                cmd.extend([','.join(usages)])
+
+            if basic_constraints_ext:
+
+                cmd.extend(['-2', hash_alg])
+
+                # Is this a CA certificate [y/N]?
+                if basic_constraints_ext['ca']:
+                    keystroke += 'y'
+
+                keystroke += '\n'
+
+                # Enter the path length constraint, enter to skip [<0 for unlimited path]:
+                if basic_constraints_ext['path_length'] is not None:
+                    keystroke += basic_constraints_ext['path_length']
+
+                keystroke += '\n'
+
+                # Is this a critical extension [y/N]?
+                if basic_constraints_ext['critical']:
+                    keystroke += 'y'
+
+                keystroke += '\n'
+
             # generate binary request
-            subprocess.check_call(cmd)
+            p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                                 stderr=subprocess.STDOUT)
+
+            p.communicate(keystroke)
+
+            rc = p.wait()
+
+            if rc:
+                raise Exception('Failed to generate certificate request. RC: %d' % rc)
 
             # encode binary request in base-64
             b64_request_file = os.path.join(tmpdir, 'request.b64')
diff --git a/base/server/python/pki/server/deployment/scriptlets/configuration.py b/base/server/python/pki/server/deployment/scriptlets/configuration.py
index 373b58ef45cf84fd5aa0be1856cff5ee23b13aba..6da08c587086260ac0b9af212fbf0ecf66e927fb 100644
--- a/base/server/python/pki/server/deployment/scriptlets/configuration.py
+++ b/base/server/python/pki/server/deployment/scriptlets/configuration.py
@@ -98,6 +98,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
             if external and step_one:  # external CA step 1 only
 
                 # Determine CA signing key type and algorithm
+
                 key_type = deployer.mdict['pki_ca_signing_key_type']
                 key_alg = deployer.mdict['pki_ca_signing_key_algorithm']
 
@@ -125,19 +126,38 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
 
                 # If filename specified, generate CA cert request and
                 # import it into CS.cfg.
+
                 external_csr_path = deployer.mdict['pki_external_csr_path']
                 if external_csr_path:
+
                     config.pki_log.info(
                         "generating CA signing certificate request in %s",
                         external_csr_path,
                         extra=config.PKI_INDENTATION_LEVEL_2)
+
+                    basic_constraints_ext = {
+                        'ca': True,
+                        'path_length': None,
+                        'critical': True
+                    }
+
+                    key_usage_ext = {
+                        'digitalSignature': True,
+                        'nonRepudiation': True,
+                        'certSigning': True,
+                        'crlSigning': True,
+                        'critical': True
+                    }
+
                     nssdb.create_request(
                         subject_dn=deployer.mdict['pki_ca_signing_subject_dn'],
                         request_file=external_csr_path,
                         key_type=key_type,
                         key_size=key_size,
                         curve=curve,
-                        hash_alg=hash_alg)
+                        hash_alg=hash_alg,
+                        basic_constraints_ext=basic_constraints_ext,
+                        key_usage_ext=key_usage_ext)
 
                     with open(external_csr_path) as f:
                         signing_csr = f.read()
@@ -148,6 +168,7 @@ class PkiScriptlet(pkiscriptlet.AbstractBasePkiScriptlet):
 
                 # This is needed by IPA to detect step 1 completion.
                 # See is_step_one_done() in ipaserver/install/cainstance.py.
+
                 subsystem.config['preop.ca.type'] = 'otherca'
 
                 subsystem.save()
-- 
2.4.11

