From f81e6ddd2c3235ecc3569b26de9483d182a242c7 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Fri, 8 Apr 2016 22:23:42 +1000 Subject: [PATCH] Lightweight CAs: add IPACustodiaKeyRetriever Add 'IPACustodiaKeyRetriever', a 'KeyRetriever' implementation for use when Dogtag is deployed as a FreeIPA CA. The Java class invokes 'pki-ipa-key-retriever', a Python script that retrieves lightweight CA keys from the Custodia server on a replica that possesses the keys. 'pki-ipa-key-retriever' depends on FreeIPA libraries, FreeIPA server configuration, and Kerberos and Custodia keys owned by 'pkiuser'. Part of: https://fedorahosted.org/pki/ticket/1625 --- base/ca/src/CMakeLists.txt | 9 ++- .../com/netscape/ca/IPACustodiaKeyRetriever.java | 74 ++++++++++++++++++++++ base/server/CMakeLists.txt | 11 ++++ base/server/libexec/pki-ipa-retrieve-key | 42 ++++++++++++ specs/pki-core.spec | 1 + 5 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 base/ca/src/com/netscape/ca/IPACustodiaKeyRetriever.java create mode 100755 base/server/libexec/pki-ipa-retrieve-key diff --git a/base/ca/src/CMakeLists.txt b/base/ca/src/CMakeLists.txt index 5b805e1b3a5eddb46d17178ca2ac204c36ae5680..1817dacfbacaeb2635db2550e32ff62c26d628ef 100644 --- a/base/ca/src/CMakeLists.txt +++ b/base/ca/src/CMakeLists.txt @@ -24,6 +24,13 @@ find_file(COMMONS_CODEC_JAR /usr/share/java ) +find_file(COMMONS_IO_JAR + NAMES + commons-io.jar + PATHS + /usr/share/java +) + find_file(COMMONS_LANG_JAR NAMES commons-lang.jar @@ -73,7 +80,7 @@ javac(pki-ca-classes com/netscape/ca/*.java org/dogtagpki/server/ca/*.java CLASSPATH - ${COMMONS_CODEC_JAR} ${COMMONS_LANG_JAR} + ${COMMONS_CODEC_JAR} ${COMMONS_IO_JAR} ${COMMONS_LANG_JAR} ${JSS_JAR} ${SYMKEY_JAR} ${LDAPJDK_JAR} ${SERVLET_JAR} ${TOMCAT_CATALINA_JAR} diff --git a/base/ca/src/com/netscape/ca/IPACustodiaKeyRetriever.java b/base/ca/src/com/netscape/ca/IPACustodiaKeyRetriever.java new file mode 100644 index 0000000000000000000000000000000000000000..59faa58a94ff4e616dc040c635ca96014828ec00 --- /dev/null +++ b/base/ca/src/com/netscape/ca/IPACustodiaKeyRetriever.java @@ -0,0 +1,74 @@ +// --- 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.ca; + +import java.lang.Process; +import java.lang.ProcessBuilder; +import java.util.Collection; +import java.util.Stack; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.ArrayUtils; + +import com.netscape.certsrv.apps.CMS; + +public class IPACustodiaKeyRetriever implements KeyRetriever { + public Result retrieveKey(String nickname, Collection hosts) { + CMS.debug("Running IPACustodiaKeyRetriever"); + + Stack command = new Stack<>(); + command.push("/usr/libexec/pki-ipa-retrieve-key"); + command.push(nickname); + + for (String host : hosts) { + command.push(host); + CMS.debug("About to execute command: " + command); + ProcessBuilder pb = new ProcessBuilder(command); + try { + Process p = pb.start(); + int exitValue = p.waitFor(); + if (exitValue != 0) + continue; + + /* Custodia returns a PKCS #12 object and the + * password to import it. These values are output + * by the Python 'pki-ipa-retrieve-key' program, + * separated by a null byte (password first) + */ + + byte[] output = IOUtils.toByteArray(p.getInputStream()); + int splitIndex = ArrayUtils.indexOf(output, (byte) 0); + if (splitIndex == ArrayUtils.INDEX_NOT_FOUND) { + CMS.debug("Invalid output: null byte not found"); + continue; + } + return new Result( + new String(ArrayUtils.subarray(output, 0, splitIndex)), + ArrayUtils.subarray(output, splitIndex + 1, output.length) + ); + } catch (Throwable e) { + CMS.debug("Caught exception while executing command: " + e); + } finally { + command.pop(); + } + } + CMS.debug("Failed to retrieve key from any host."); + return null; + } +} diff --git a/base/server/CMakeLists.txt b/base/server/CMakeLists.txt index 5a6aea96a2317655fb454967f9f218020443bcb8..9e5b27833c8d023e63320c43d64ad64b0055c254 100644 --- a/base/server/CMakeLists.txt +++ b/base/server/CMakeLists.txt @@ -81,6 +81,17 @@ install( install( DIRECTORY + libexec/ + DESTINATION + ${LIBEXEC_INSTALL_DIR} + FILE_PERMISSIONS + OWNER_EXECUTE OWNER_WRITE OWNER_READ + GROUP_EXECUTE GROUP_READ + WORLD_EXECUTE WORLD_READ +) + +install( + DIRECTORY upgrade DESTINATION ${DATA_INSTALL_DIR}/server/ diff --git a/base/server/libexec/pki-ipa-retrieve-key b/base/server/libexec/pki-ipa-retrieve-key new file mode 100755 index 0000000000000000000000000000000000000000..757414ce7be43d675831f29d4811b94101f494c0 --- /dev/null +++ b/base/server/libexec/pki-ipa-retrieve-key @@ -0,0 +1,42 @@ +#!/usr/bin/python + +from __future__ import print_function + +import ConfigParser +import base64 +import sys + +from jwcrypto.common import json_decode + +from ipaplatform.paths import paths +from ipapython.secrets.client import CustodiaClient + +conf = ConfigParser.ConfigParser() +conf.read(paths.IPA_DEFAULT_CONF) +hostname = conf.get('global', 'host') +realm = conf.get('global', 'realm') + +keyname = "ca/" + sys.argv[1] +servername = sys.argv[2] + +client_keyfile = "/etc/pki/pki-tomcat/dogtag-ipa-custodia.keys" +client_keytab = "/etc/pki/pki-tomcat/dogtag-ipa-custodia.keytab" + +client = CustodiaClient( + client=hostname, server=servername, realm=realm, + ldap_uri="ldaps://" + hostname, + client_servicename='dogtag-ipa-custodia', + keyfile=client_keyfile, keytab=client_keytab, + ) + +result_json = client.fetch_key(keyname, store=False) +result = json_decode(result_json) +password = result["export password"] +pkcs12 = base64.b64decode(result["pkcs12 data"]) + +# Custodia returns a PKCS #12 object and the password to import it. +# These values are output separated by a null byte (password first), +# and read by the Java IPACustodiaKeyRetriever that invoked this +# program. + +print(password, pkcs12, sep='\0', end='') diff --git a/specs/pki-core.spec b/specs/pki-core.spec index b7e59a43f65ef8740e5fac42071240ad3c4bce9a..63c44329872659ca208bc300baad39fc87214ee8 100644 --- a/specs/pki-core.spec +++ b/specs/pki-core.spec @@ -1007,6 +1007,7 @@ systemctl daemon-reload %{_sbindir}/pki-server %{_sbindir}/pki-server-nuxwdog %{_sbindir}/pki-server-upgrade +%{_libexecdir}/pki-ipa-retrieve-key %{python2_sitelib}/pki/server/ %dir %{_datadir}/pki/deployment %{_datadir}/pki/deployment/config/ -- 2.5.5