diff options
4 files changed, 315 insertions, 0 deletions
diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCLI.java index f09ea74e9..8bafd84f6 100644 --- a/base/java-tools/src/com/netscape/cmstools/client/ClientCLI.java +++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCLI.java @@ -39,6 +39,7 @@ public class ClientCLI extends CLI { addModule(new ClientCertRemoveCLI(this)); addModule(new ClientCertRequestCLI(this)); addModule(new ClientCertShowCLI(this)); + addModule(new ClientCertValidateCLI(this)); } public String getFullName() { diff --git a/base/java-tools/src/com/netscape/cmstools/client/ClientCertValidateCLI.java b/base/java-tools/src/com/netscape/cmstools/client/ClientCertValidateCLI.java new file mode 100644 index 000000000..3988c71e2 --- /dev/null +++ b/base/java-tools/src/com/netscape/cmstools/client/ClientCertValidateCLI.java @@ -0,0 +1,194 @@ +// --- BEGIN COPYRIGHT BLOCK --- +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; version 2 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// (C) 2016 Red Hat, Inc. +// All rights reserved. +// --- END COPYRIGHT BLOCK --- + +package com.netscape.cmstools.client; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.lang.StringUtils; +import org.mozilla.jss.CryptoManager; +import org.mozilla.jss.CryptoManager.CertificateUsage; + +import com.netscape.cmstools.cli.CLI; + +/** + * @author Ade Lee + */ +public class ClientCertValidateCLI extends CLI { + + public ClientCLI clientCLI; + + public ClientCertValidateCLI(ClientCLI clientCLI) { + super("cert-validate", "Validate certificate", clientCLI); + this.clientCLI = clientCLI; + + createOptions(); + } + + public void createOptions() { + Option option = new Option(null, "certusage", true, "Certificate usage."); + option.setArgName("certusage"); + options.addOption(option); + } + + public void printHelp() { + formatter.printHelp(getFullName() + " nickname", options); + } + + public void execute(String[] args) throws Exception { + // Always check for "--help" prior to parsing + if (Arrays.asList(args).contains("--help")) { + // Display usage + printHelp(); + System.exit(0); + } + + CommandLine cmd = null; + + try { + cmd = parser.parse(options, args); + + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + printHelp(); + System.exit(-1); + } + + String[] cmdArgs = cmd.getArgs(); + + if (cmdArgs.length != 1) { + System.err.println("Error: Invalid number of arguments."); + printHelp(); + System.exit(-1); + } + + // Get nickname from command argument. + String nickname = cmdArgs[0]; + + // get usages from options + String certusage = cmd.getOptionValue("certusage"); + boolean isValid = false; + + try { + isValid = verifySystemCertByNickname(nickname, certusage); + } catch (Exception e) { + System.err.println("Certificate verification failed: " + e); + isValid = false; + } + + if (isValid) { + System.exit(0); + } else { + System.exit(1); + } + } + + public boolean verifySystemCertByNickname(String nickname, String certusage) throws Exception { + CertificateUsage cu = getCertificateUsage(certusage); + int ccu = 0; + + if (cu == null) { + throw new Exception("Unsupported certificate usage " + certusage + + " in certificate " + nickname); + } + + CryptoManager cm = CryptoManager.getInstance(); + if (cu.getUsage() != CryptoManager.CertificateUsage.CheckAllUsages.getUsage()) { + if (cm.isCertValid(nickname, true, cu)) { + System.out.println("Valid certificate: " + nickname); + return true; + } else { + System.out.println("Invalid certificate: " + nickname); + return false; + } + + } else { + // check all possible usages + ccu = cm.isCertValid(nickname, true); + if (ccu == CertificateUsage.basicCertificateUsages) { + /* cert is good for nothing */ + System.out.println("Cert is good for nothing: " + nickname); + return false; + } else { + List<String> usages = new ArrayList<String>(); + if ((ccu & CryptoManager.CertificateUsage.SSLServer.getUsage()) != 0) + usages.add("SSLServer"); + if ((ccu & CryptoManager.CertificateUsage.SSLClient.getUsage()) != 0) + usages.add("SSLClient"); + if ((ccu & CryptoManager.CertificateUsage.SSLServerWithStepUp.getUsage()) != 0) + usages.add("SSLServerWithStepUp"); + if ((ccu & CryptoManager.CertificateUsage.SSLCA.getUsage()) != 0) + usages.add("SSLCA"); + if ((ccu & CryptoManager.CertificateUsage.EmailSigner.getUsage()) != 0) + usages.add("EmailSigner"); + if ((ccu & CryptoManager.CertificateUsage.EmailRecipient.getUsage()) != 0) + usages.add("EmailRecipient"); + if ((ccu & CryptoManager.CertificateUsage.ObjectSigner.getUsage()) != 0) + usages.add("ObjectSigner"); + if ((ccu & CryptoManager.CertificateUsage.UserCertImport.getUsage()) != 0) + usages.add("UserCertImport"); + if ((ccu & CryptoManager.CertificateUsage.VerifyCA.getUsage()) != 0) + usages.add("VerifyCA"); + if ((ccu & CryptoManager.CertificateUsage.ProtectedObjectSigner.getUsage()) != 0) + usages.add("ProtectedObjectSigner"); + if ((ccu & CryptoManager.CertificateUsage.StatusResponder.getUsage()) != 0) + usages.add("StatusResponder"); + if ((ccu & CryptoManager.CertificateUsage.AnyCA.getUsage()) != 0) + usages.add("AnyCA"); + System.out.println("Cert has the following usages: " + StringUtils.join(usages, ',')); + return true; + } + } + } + + public CertificateUsage getCertificateUsage(String certusage) { + CertificateUsage cu = null; + if ((certusage == null) || certusage.equals("")) + cu = CryptoManager.CertificateUsage.CheckAllUsages; + else if (certusage.equalsIgnoreCase("CheckAllUsages")) + cu = CryptoManager.CertificateUsage.CheckAllUsages; + else if (certusage.equalsIgnoreCase("SSLServer")) + cu = CryptoManager.CertificateUsage.SSLServer; + else if (certusage.equalsIgnoreCase("SSLServerWithStepUp")) + cu = CryptoManager.CertificateUsage.SSLServerWithStepUp; + else if (certusage.equalsIgnoreCase("SSLClient")) + cu = CryptoManager.CertificateUsage.SSLClient; + else if (certusage.equalsIgnoreCase("SSLCA")) + cu = CryptoManager.CertificateUsage.SSLCA; + else if (certusage.equalsIgnoreCase("AnyCA")) + cu = CryptoManager.CertificateUsage.AnyCA; + else if (certusage.equalsIgnoreCase("StatusResponder")) + cu = CryptoManager.CertificateUsage.StatusResponder; + else if (certusage.equalsIgnoreCase("ObjectSigner")) + cu = CryptoManager.CertificateUsage.ObjectSigner; + else if (certusage.equalsIgnoreCase("UserCertImport")) + cu = CryptoManager.CertificateUsage.UserCertImport; + else if (certusage.equalsIgnoreCase("ProtectedObjectSigner")) + cu = CryptoManager.CertificateUsage.ProtectedObjectSigner; + else if (certusage.equalsIgnoreCase("VerifyCA")) + cu = CryptoManager.CertificateUsage.VerifyCA; + else if (certusage.equalsIgnoreCase("EmailSigner")) + cu = CryptoManager.CertificateUsage.EmailSigner; + + return cu; + } +} diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py index 64688b3c4..1c590c37e 100644 --- a/base/server/python/pki/server/__init__.py +++ b/base/server/python/pki/server/__init__.py @@ -156,6 +156,8 @@ class PKISubsystem(object): '%s.%s.cert' % (self.name, cert_id), None) cert['request'] = self.config.get( '%s.%s.certreq' % (self.name, cert_id), None) + cert['certusage'] = self.config.get( + '%s.cert.%s.certusage' % (self.name, cert_id), None) return cert def update_subsystem_cert(self, cert): diff --git a/base/server/python/pki/server/cli/subsystem.py b/base/server/python/pki/server/cli/subsystem.py index 03d48f926..6d60468a6 100644 --- a/base/server/python/pki/server/cli/subsystem.py +++ b/base/server/python/pki/server/cli/subsystem.py @@ -24,8 +24,11 @@ import base64 import getopt import getpass import nss.nss as nss +import os import string +import subprocess import sys +from tempfile import mkstemp import pki.cli import pki.nssdb @@ -299,6 +302,7 @@ class SubsystemCertCLI(pki.cli.CLI): self.add_module(SubsystemCertShowCLI()) self.add_module(SubsystemCertExportCLI()) self.add_module(SubsystemCertUpdateCLI()) + self.add_module(SubsystemCertValidateCLI()) @staticmethod def print_subsystem_cert(cert, show_all=False): @@ -713,3 +717,117 @@ class SubsystemCertUpdateCLI(pki.cli.CLI): self.print_message('Updated "%s" subsystem certificate' % cert_id) SubsystemCertCLI.print_subsystem_cert(subsystem_cert) + + +class SubsystemCertValidateCLI(pki.cli.CLI): + + def __init__(self): + super(SubsystemCertValidateCLI, self).__init__( + 'validate', 'Validate subsystem certificates') + + def usage(self): + print('Usage: pki-server subsystem-cert-validate [OPTIONS] <subsystem ID> [<cert_id>]') + print() + print(' -i, --instance <instance ID> Instance ID (default: pki-tomcat).') + print(' -v, --verbose Run in verbose mode.') + print(' --help Show help message.') + print() + + def execute(self, argv): + + try: + opts, args = getopt.gnu_getopt(argv, 'i:v', [ + 'instance=', + 'verbose', 'help']) + + except getopt.GetoptError as e: + print('ERROR: ' + str(e)) + self.usage() + sys.exit(1) + + if len(args) < 1: + print('ERROR: missing subsystem ID') + self.usage() + sys.exit(1) + + subsystem_name = args[0] + + if len(args) >=2: + cert_id = args[1] + else: + cert_id = None + + instance_name = 'pki-tomcat' + + for o, a in opts: + if o in ('-i', '--instance'): + instance_name = a + + elif o in ('-v', '--verbose'): + self.set_verbose(True) + + elif o == '--help': + self.print_help() + sys.exit() + + else: + self.print_message('ERROR: unknown option ' + o) + self.usage() + sys.exit(1) + + instance = pki.server.PKIInstance(instance_name) + instance.load() + + subsystem = instance.get_subsystem(subsystem_name) + + if cert_id is not None: + certs = [subsystem.get_subsystem_cert(cert_id)] + else: + certs = subsystem.find_system_certs() + + certs_valid = True + for cert in certs: + token = cert['token'] + + # get token password and store in temporary file + if token == 'Internal Key Storage Token': + passwd = instance.get_password('internal') + else: + passwd = instance.get_password("hardware-%s" % token) + + pwfile_handle, pwfile_path = mkstemp() + os.write(pwfile_handle, passwd) + os.close(pwfile_handle) + + cmd = ['pki', '-d', instance.nssdb_dir, + '-W', pwfile_path ] + + if token != 'Internal Key Storage Token': + cmd.extend(['--token', token]) + + cmd.extend( + ['client-cert-validate', + cert['nickname'], + '--certusage', cert['certusage']] + ) + + try: + subprocess.check_output(cmd, stderr=subprocess.STDOUT) + self.print_message("Valid certificate : %s" %cert['nickname']) + except subprocess.CalledProcessError as e: + certs_valid = False + if e.returncode == 1: + self.print_message("Invalid certificate: %s" + % cert['nickname']) + else: + self.print_message("Error in validating certificate: %s" + % cert['nickname']) + self.print_message(e.output) + finally: + os.unlink(pwfile_path) + + if certs_valid: + sys.exit(0) + else: + sys.exit(1) + |