Blame SOURCES/pki-core-added-support-for-secure-database-connection-in-CLI.patch

efcdb2
commit f153bd8a455953698e8af5085cd3cd7b368b1247
efcdb2
Author: Endi S. Dewata <edewata@redhat.com>
efcdb2
Date:   Fri Sep 4 06:30:27 2015 +0200
efcdb2
efcdb2
    Added support for secure database connection in CLI.
efcdb2
    
efcdb2
    The pki-server subsystem-cert-update has been modified to support
efcdb2
    secure database connection with client certificate authentication.
efcdb2
    The certificate and the private key will be exported temporarily
efcdb2
    into PEM files so python-ldap can use them.
efcdb2
    
efcdb2
    The pki client-cert-show has been modified to provide an option
efcdb2
    to export client certificate's private key.
efcdb2
    
efcdb2
    https://fedorahosted.org/pki/ticket/1551
efcdb2
efcdb2
diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertShowCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertShowCLI.java
efcdb2
index f79501c..e44fae7 100644
efcdb2
--- a/base/java-tools/src/com/netscape/cmstools/client/ClientCertShowCLI.java
efcdb2
+++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertShowCLI.java
efcdb2
@@ -29,10 +29,8 @@ import org.apache.commons.lang.RandomStringUtils;
efcdb2
 import org.apache.commons.lang.StringUtils;
efcdb2
 import org.mozilla.jss.crypto.X509Certificate;
efcdb2
 
efcdb2
-import com.netscape.certsrv.cert.CertData;
efcdb2
 import com.netscape.cmstools.cli.CLI;
efcdb2
 import com.netscape.cmstools.cli.MainCLI;
efcdb2
-import com.netscape.cmsutil.util.Utils;
efcdb2
 
efcdb2
 /**
efcdb2
  * @author Endi S. Dewata
efcdb2
@@ -57,6 +55,10 @@ public class ClientCertShowCLI extends CLI {
efcdb2
         option.setArgName("path");
efcdb2
         options.addOption(option);
efcdb2
 
efcdb2
+        option = new Option(null, "private-key", true, "PEM file to store the private key.");
efcdb2
+        option.setArgName("path");
efcdb2
+        options.addOption(option);
efcdb2
+
efcdb2
         option = new Option(null, "client-cert", true, "PEM file to store the certificate and the private key.");
efcdb2
         option.setArgName("path");
efcdb2
         options.addOption(option);
efcdb2
@@ -107,90 +109,82 @@ public class ClientCertShowCLI extends CLI {
efcdb2
 
efcdb2
         String nickname = cmdArgs[0];
efcdb2
         String certPath = cmd.getOptionValue("cert");
efcdb2
+        String privateKeyPath = cmd.getOptionValue("private-key");
efcdb2
+        String clientCertPath = cmd.getOptionValue("client-cert");
efcdb2
         String pkcs12Path = cmd.getOptionValue("pkcs12");
efcdb2
         String pkcs12Password = cmd.getOptionValue("pkcs12-password");
efcdb2
-        String clientCertPath = cmd.getOptionValue("client-cert");
efcdb2
-
efcdb2
-        if (certPath != null) {
efcdb2
-
efcdb2
-            if (verbose) System.out.println("Exporting certificate to " + clientCertPath + ".");
efcdb2
-
efcdb2
-            // late initialization
efcdb2
-            mainCLI.init();
efcdb2
 
efcdb2
-            client = mainCLI.getClient();
efcdb2
-            X509Certificate cert = client.getCert(nickname);
efcdb2
+        File pkcs12File;
efcdb2
 
efcdb2
-            try (PrintWriter out = new PrintWriter(new FileWriter(certPath))) {
efcdb2
-                out.println(CertData.HEADER);
efcdb2
-                out.println(Utils.base64encode(cert.getEncoded()));
efcdb2
-                out.println(CertData.FOOTER);
efcdb2
-            }
efcdb2
+        if (pkcs12Path != null) {
efcdb2
+            // exporting certificate to PKCS #12 file
efcdb2
 
efcdb2
-        } else if (pkcs12Path != null) {
efcdb2
-
efcdb2
-            if (verbose) System.out.println("Exporting certificate chain and private key to " + pkcs12Path + ".");
efcdb2
+            pkcs12File = new File(pkcs12Path);
efcdb2
 
efcdb2
             if (pkcs12Password == null) {
efcdb2
                 throw new Exception("Missing PKCS #12 password");
efcdb2
             }
efcdb2
 
efcdb2
-            // store password into a temporary file
efcdb2
-            File pkcs12PasswordFile = File.createTempFile("pki-client-cert-show-", ".pwd");
efcdb2
-            pkcs12PasswordFile.deleteOnExit();
efcdb2
+        } else if (certPath != null || clientCertPath != null || privateKeyPath != null) {
efcdb2
+            // exporting certificate and/or private key to PEM files using temporary PKCS #12 file
efcdb2
 
efcdb2
-            try (PrintWriter out = new PrintWriter(new FileWriter(pkcs12PasswordFile))) {
efcdb2
-                out.print(pkcs12Password);
efcdb2
-            }
efcdb2
+            // prepare temporary PKCS #12 file
efcdb2
+            pkcs12File = File.createTempFile("pki-client-cert-show-", ".p12");
efcdb2
+            pkcs12File.deleteOnExit();
efcdb2
 
efcdb2
-            // export certificate chain and private key into PKCS #12 file
efcdb2
-            exportPKCS12(
efcdb2
-                    mainCLI.certDatabase.getAbsolutePath(),
efcdb2
-                    mainCLI.config.getCertPassword(),
efcdb2
-                    pkcs12Path,
efcdb2
-                    pkcs12PasswordFile.getAbsolutePath(),
efcdb2
-                    nickname);
efcdb2
+            // generate random password
efcdb2
+            pkcs12Password = RandomStringUtils.randomAlphanumeric(16);
efcdb2
 
efcdb2
-        } else if (clientCertPath != null) {
efcdb2
+        } else {
efcdb2
+            // displaying certificate info
efcdb2
 
efcdb2
-            if (verbose) System.out.println("Exporting client certificate and private key to " + clientCertPath + ".");
efcdb2
+            mainCLI.init();
efcdb2
 
efcdb2
-            // generate random PKCS #12 password
efcdb2
-            pkcs12Password = RandomStringUtils.randomAlphanumeric(16);
efcdb2
+            client = mainCLI.getClient();
efcdb2
+            X509Certificate cert = client.getCert(nickname);
efcdb2
+
efcdb2
+            ClientCLI.printCertInfo(cert);
efcdb2
+            return;
efcdb2
+        }
efcdb2
 
efcdb2
-            // store password into a temporary file
efcdb2
-            File pkcs12PasswordFile = File.createTempFile("pki-client-cert-show-", ".pwd");
efcdb2
-            pkcs12PasswordFile.deleteOnExit();
efcdb2
+        // store password into a temporary file
efcdb2
+        File pkcs12PasswordFile = File.createTempFile("pki-client-cert-show-", ".pwd");
efcdb2
+        pkcs12PasswordFile.deleteOnExit();
efcdb2
 
efcdb2
-            try (PrintWriter out = new PrintWriter(new FileWriter(pkcs12PasswordFile))) {
efcdb2
-                out.print(pkcs12Password);
efcdb2
-            }
efcdb2
+        try (PrintWriter out = new PrintWriter(new FileWriter(pkcs12PasswordFile))) {
efcdb2
+            out.print(pkcs12Password);
efcdb2
+        }
efcdb2
 
efcdb2
-            // export certificate chain and private key into a temporary PKCS #12 file
efcdb2
-            File pkcs12File = File.createTempFile("pki-client-cert-show-", ".p12");
efcdb2
-            pkcs12File.deleteOnExit();
efcdb2
+        if (verbose) System.out.println("Exporting certificate chain and private key to " + pkcs12File + ".");
efcdb2
+        exportPKCS12(
efcdb2
+                mainCLI.certDatabase.getAbsolutePath(),
efcdb2
+                mainCLI.config.getCertPassword(),
efcdb2
+                pkcs12File.getAbsolutePath(),
efcdb2
+                pkcs12PasswordFile.getAbsolutePath(),
efcdb2
+                nickname);
efcdb2
 
efcdb2
-            exportPKCS12(
efcdb2
-                    mainCLI.certDatabase.getAbsolutePath(),
efcdb2
-                    mainCLI.config.getCertPassword(),
efcdb2
+        if (certPath != null) {
efcdb2
+            if (verbose) System.out.println("Exporting certificate to " + certPath + ".");
efcdb2
+            exportCertificate(
efcdb2
                     pkcs12File.getAbsolutePath(),
efcdb2
                     pkcs12PasswordFile.getAbsolutePath(),
efcdb2
-                    nickname);
efcdb2
+                    certPath);
efcdb2
+        }
efcdb2
 
efcdb2
-            // export client certificate and private key into a PEM file
efcdb2
-            exportClientCertificate(
efcdb2
+        if (privateKeyPath != null) {
efcdb2
+            if (verbose) System.out.println("Exporting private key to " + privateKeyPath + ".");
efcdb2
+            exportPrivateKey(
efcdb2
                     pkcs12File.getAbsolutePath(),
efcdb2
                     pkcs12PasswordFile.getAbsolutePath(),
efcdb2
-                    clientCertPath);
efcdb2
-
efcdb2
-        } else {
efcdb2
-            // late initialization
efcdb2
-            mainCLI.init();
efcdb2
-
efcdb2
-            client = mainCLI.getClient();
efcdb2
-            X509Certificate cert = client.getCert(nickname);
efcdb2
+                    privateKeyPath);
efcdb2
+        }
efcdb2
 
efcdb2
-            ClientCLI.printCertInfo(cert);
efcdb2
+        if (clientCertPath != null) {
efcdb2
+            if (verbose) System.out.println("Exporting client certificate and private key to " + clientCertPath + ".");
efcdb2
+            exportClientCertificateAndPrivateKey(
efcdb2
+                    pkcs12File.getAbsolutePath(),
efcdb2
+                    pkcs12PasswordFile.getAbsolutePath(),
efcdb2
+                    clientCertPath);
efcdb2
         }
efcdb2
     }
efcdb2
 
efcdb2
@@ -218,7 +212,53 @@ public class ClientCertShowCLI extends CLI {
efcdb2
         }
efcdb2
     }
efcdb2
 
efcdb2
-    public void exportClientCertificate(
efcdb2
+    public void exportCertificate(
efcdb2
+            String pkcs12Path,
efcdb2
+            String pkcs12PasswordPath,
efcdb2
+            String certPath) throws Exception {
efcdb2
+
efcdb2
+        String[] command = {
efcdb2
+                "/bin/openssl",
efcdb2
+                "pkcs12",
efcdb2
+                "-clcerts", // certificate only
efcdb2
+                "-nokeys",
efcdb2
+                "-in",      pkcs12Path,
efcdb2
+                "-passin",  "file:" + pkcs12PasswordPath,
efcdb2
+                "-out",     certPath
efcdb2
+        };
efcdb2
+
efcdb2
+        try {
efcdb2
+            run(command);
efcdb2
+
efcdb2
+        } catch (Exception e) {
efcdb2
+            throw new Exception("Unable to export certificate", e);
efcdb2
+        }
efcdb2
+    }
efcdb2
+
efcdb2
+    public void exportPrivateKey(
efcdb2
+            String pkcs12Path,
efcdb2
+            String pkcs12PasswordPath,
efcdb2
+            String privateKeyPath) throws Exception {
efcdb2
+
efcdb2
+        String[] command = {
efcdb2
+                "/bin/openssl",
efcdb2
+                "pkcs12",
efcdb2
+                "-nocerts", // private key only
efcdb2
+                "-nodes",   // no encryption
efcdb2
+                "-in",      pkcs12Path,
efcdb2
+                "-passin",  "file:" + pkcs12PasswordPath,
efcdb2
+                "-out",     privateKeyPath
efcdb2
+        };
efcdb2
+
efcdb2
+        try {
efcdb2
+            run(command);
efcdb2
+
efcdb2
+        } catch (Exception e) {
efcdb2
+            throw new Exception("Unable to export private key", e);
efcdb2
+        }
efcdb2
+    }
efcdb2
+
efcdb2
+    public void exportClientCertificateAndPrivateKey(
efcdb2
             String pkcs12Path,
efcdb2
             String pkcs12PasswordPath,
efcdb2
             String clientCertPath) throws Exception {
efcdb2
@@ -226,7 +266,7 @@ public class ClientCertShowCLI extends CLI {
efcdb2
         String[] command = {
efcdb2
                 "/bin/openssl",
efcdb2
                 "pkcs12",
efcdb2
-                "-clcerts", // client certificate only
efcdb2
+                "-clcerts", // client certificate and private key
efcdb2
                 "-nodes",   // no encryption
efcdb2
                 "-in",      pkcs12Path,
efcdb2
                 "-passin",  "file:" + pkcs12PasswordPath,
efcdb2
@@ -237,7 +277,7 @@ public class ClientCertShowCLI extends CLI {
efcdb2
             run(command);
efcdb2
 
efcdb2
         } catch (Exception e) {
efcdb2
-            throw new Exception("Unable to export client certificate", e);
efcdb2
+            throw new Exception("Unable to export client certificate and private key", e);
efcdb2
         }
efcdb2
     }
efcdb2
 
efcdb2
diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py
efcdb2
index d004465..89d4acf 100644
efcdb2
--- a/base/server/python/pki/server/__init__.py
efcdb2
+++ b/base/server/python/pki/server/__init__.py
efcdb2
@@ -28,7 +28,9 @@ import operator
efcdb2
 import os
efcdb2
 import pwd
efcdb2
 import re
efcdb2
+import shutil
efcdb2
 import subprocess
efcdb2
+import tempfile
efcdb2
 
efcdb2
 import pki
efcdb2
 
efcdb2
@@ -162,18 +164,43 @@ class PKISubsystem(object):
efcdb2
 
efcdb2
     def open_database(self, name='internaldb'):
efcdb2
 
efcdb2
+        # TODO: add LDAPI support
efcdb2
         hostname = self.config['%s.ldapconn.host' % name]
efcdb2
         port = self.config['%s.ldapconn.port' % name]
efcdb2
-        bind_dn = self.config['%s.ldapauth.bindDN' % name]
efcdb2
+        secure = self.config['%s.ldapconn.secureConn' % name]
efcdb2
 
efcdb2
-        # TODO: add support for other authentication
efcdb2
-        # mechanisms (e.g. client cert authentication, LDAPI)
efcdb2
-        bind_password = self.instance.get_password(name)
efcdb2
+        if secure == 'true':
efcdb2
+            url = 'ldaps://%s:%s' % (hostname, port)
efcdb2
 
efcdb2
-        con = ldap.initialize('ldap://%s:%s' % (hostname, port))
efcdb2
-        con.simple_bind_s(bind_dn, bind_password)
efcdb2
+        elif secure == 'false':
efcdb2
+            url = 'ldap://%s:%s' % (hostname, port)
efcdb2
 
efcdb2
-        return con
efcdb2
+        else:
efcdb2
+            raise Exception('Invalid parameter value in %s.ldapconn.secureConn: %s' % (name, secure))
efcdb2
+
efcdb2
+        connection = PKIDatabaseConnection(url)
efcdb2
+
efcdb2
+        connection.set_security_database(self.instance.nssdb_dir)
efcdb2
+
efcdb2
+        auth_type = self.config['%s.ldapauth.authtype' % name]
efcdb2
+        if auth_type == 'BasicAuth':
efcdb2
+            connection.set_credentials(
efcdb2
+                bind_dn=self.config['%s.ldapauth.bindDN' % name],
efcdb2
+                bind_password=self.instance.get_password(name)
efcdb2
+            )
efcdb2
+
efcdb2
+        elif auth_type == 'SslClientAuth':
efcdb2
+            connection.set_credentials(
efcdb2
+                client_cert_nickname=self.config['%s.ldapauth.clientCertNickname' % name],
efcdb2
+                nssdb_password=self.instance.get_password('internal')
efcdb2
+            )
efcdb2
+
efcdb2
+        else:
efcdb2
+            raise Exception('Invalid parameter value in %s.ldapauth.authtype: %s' % (name, auth_type))
efcdb2
+
efcdb2
+        connection.open()
efcdb2
+
efcdb2
+        return connection
efcdb2
 
efcdb2
     def __repr__(self):
efcdb2
         return str(self.instance) + '/' + self.name
efcdb2
@@ -337,6 +364,64 @@ class PKIInstance(object):
efcdb2
         return self.name
efcdb2
 
efcdb2
 
efcdb2
+class PKIDatabaseConnection(object):
efcdb2
+
efcdb2
+    def __init__(self, url='ldap://localhost:389'):
efcdb2
+
efcdb2
+        self.url = url
efcdb2
+
efcdb2
+        self.nssdb_dir = None
efcdb2
+
efcdb2
+        self.bind_dn = None
efcdb2
+        self.bind_password = None
efcdb2
+
efcdb2
+        self.client_cert_nickname = None
efcdb2
+        self.nssdb_password = None
efcdb2
+
efcdb2
+        self.temp_dir = None
efcdb2
+        self.ldap = None
efcdb2
+
efcdb2
+    def set_security_database(self, nssdb_dir=None):
efcdb2
+        self.nssdb_dir = nssdb_dir
efcdb2
+
efcdb2
+    def set_credentials(self, bind_dn=None, bind_password=None,
efcdb2
+            client_cert_nickname=None, nssdb_password=None):
efcdb2
+        self.bind_dn = bind_dn
efcdb2
+        self.bind_password = bind_password
efcdb2
+        self.client_cert_nickname = client_cert_nickname
efcdb2
+        self.nssdb_password = nssdb_password
efcdb2
+
efcdb2
+    def open(self):
efcdb2
+
efcdb2
+        self.temp_dir = tempfile.mkdtemp()
efcdb2
+
efcdb2
+        if self.nssdb_dir:
efcdb2
+
efcdb2
+            ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, self.nssdb_dir)
efcdb2
+
efcdb2
+        if self.client_cert_nickname:
efcdb2
+
efcdb2
+            password_file = os.path.join(self.temp_dir, 'password.txt')
efcdb2
+            with open(password_file, 'w') as f:
efcdb2
+                f.write(self.nssdb_password)
efcdb2
+
efcdb2
+            ldap.set_option(ldap.OPT_X_TLS_CERTFILE, self.client_cert_nickname)
efcdb2
+            ldap.set_option(ldap.OPT_X_TLS_KEYFILE, password_file)
efcdb2
+
efcdb2
+        self.ldap = ldap.initialize(self.url)
efcdb2
+
efcdb2
+        if self.bind_dn and self.bind_password:
efcdb2
+            self.ldap.simple_bind_s(self.bind_dn, self.bind_password)
efcdb2
+
efcdb2
+    def close(self):
efcdb2
+
efcdb2
+        if self.ldap:
efcdb2
+            self.ldap.unbind_s()
efcdb2
+
efcdb2
+        if self.temp_dir:
efcdb2
+            shutil.rmtree(self.temp_dir)
efcdb2
+
efcdb2
+
efcdb2
 class PKIServerException(pki.PKIException):
efcdb2
 
efcdb2
     def __init__(self, message, exception=None,
efcdb2
diff --git a/base/server/python/pki/server/ca.py b/base/server/python/pki/server/ca.py
efcdb2
index 70ebf4d..31e373a 100644
efcdb2
--- a/base/server/python/pki/server/ca.py
efcdb2
+++ b/base/server/python/pki/server/ca.py
efcdb2
@@ -45,13 +45,13 @@ class CASubsystem(pki.server.PKISubsystem):
efcdb2
 
efcdb2
         con = self.open_database()
efcdb2
 
efcdb2
-        entries = con.search_s(
efcdb2
+        entries = con.ldap.search_s(
efcdb2
             'ou=ca,ou=requests,%s' % base_dn,
efcdb2
             ldap.SCOPE_ONELEVEL,
efcdb2
             search_filter,
efcdb2
             None)
efcdb2
 
efcdb2
-        con.unbind_s()
efcdb2
+        con.close()
efcdb2
 
efcdb2
         requests = []
efcdb2
         for entry in entries:
efcdb2
@@ -65,13 +65,13 @@ class CASubsystem(pki.server.PKISubsystem):
efcdb2
 
efcdb2
         con = self.open_database()
efcdb2
 
efcdb2
-        entries = con.search_s(
efcdb2
+        entries = con.ldap.search_s(
efcdb2
             'cn=%s,ou=ca,ou=requests,%s' % (request_id, base_dn),
efcdb2
             ldap.SCOPE_BASE,
efcdb2
             '(objectClass=*)',
efcdb2
             None)
efcdb2
 
efcdb2
-        con.unbind_s()
efcdb2
+        con.close()
efcdb2
 
efcdb2
         entry = entries[0]
efcdb2
         return self.create_request_object(entry)
efcdb2
diff --git a/base/server/python/pki/server/cli/subsystem.py b/base/server/python/pki/server/cli/subsystem.py
efcdb2
index fc89c27..19db203 100644
efcdb2
--- a/base/server/python/pki/server/cli/subsystem.py
efcdb2
+++ b/base/server/python/pki/server/cli/subsystem.py
efcdb2
@@ -104,7 +104,7 @@ class SubsystemFindCLI(pki.cli.CLI):
efcdb2
             if first:
efcdb2
                 first = False
efcdb2
             else:
efcdb2
-                print
efcdb2
+                print()
efcdb2
 
efcdb2
             SubsystemCLI.print_subsystem(subsystem)
efcdb2
 
efcdb2
diff --git a/base/server/upgrade/10.2.0/01-AddTLSRangeSupport b/base/server/upgrade/10.2.0/01-AddTLSRangeSupport
efcdb2
index b5b83f4..e225924 100755
efcdb2
--- a/base/server/upgrade/10.2.0/01-AddTLSRangeSupport
efcdb2
+++ b/base/server/upgrade/10.2.0/01-AddTLSRangeSupport
efcdb2
@@ -29,7 +29,7 @@ import pki.server.upgrade
efcdb2
 class AddTLSRangeSupport(pki.server.upgrade.PKIServerUpgradeScriptlet):
efcdb2
 
efcdb2
     def __init__(self):
efcdb2
-
efcdb2
+        super(AddTLSRangeSupport, self).__init__()
efcdb2
         self.message = 'Add TLS Range Support'
efcdb2
 
efcdb2
         self.parser = etree.XMLParser(remove_blank_text=True)