diff --git a/SOURCES/pki-core-add-profile-component-that-copies-CN-to-SAN.patch b/SOURCES/pki-core-add-profile-component-that-copies-CN-to-SAN.patch new file mode 100644 index 0000000..a0729af --- /dev/null +++ b/SOURCES/pki-core-add-profile-component-that-copies-CN-to-SAN.patch @@ -0,0 +1,484 @@ +From fa65ec19458bbd767f54e52f61d920b529936e19 Mon Sep 17 00:00:00 2001 +From: Fraser Tweedale +Date: Wed, 1 Feb 2017 16:15:39 +1000 +Subject: [PATCH 1/5] DNSName: add method to get value + +To implement a profile default that copies CN to SAN dNSName, we +need to examine existing dNSName values. To support this, add the +'getValue()' method to 'DNSName'. + +Part of: https://fedorahosted.org/pki/ticket/1710 + +(cherry picked from commit f371114134ee3b6a83b747eecf46e001080b1e9c) +(cherry picked from commit a30f25cbb496b6e24b417a02602e0cdbe079cbd3) +--- + base/util/src/netscape/security/x509/DNSName.java | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/base/util/src/netscape/security/x509/DNSName.java b/base/util/src/netscape/security/x509/DNSName.java +index 361c235..2161adf 100644 +--- a/base/util/src/netscape/security/x509/DNSName.java ++++ b/base/util/src/netscape/security/x509/DNSName.java +@@ -79,4 +79,12 @@ public class DNSName implements GeneralNameInterface { + public String toString() { + return ("DNSName: " + name); + } ++ ++ /** ++ * Get the raw DNSName value. ++ */ ++ public String getValue() { ++ return name; ++ } ++ + } +-- +1.8.3.1 + + +From 6fa86d4f50b5846f5d6f8a12797f61dd5b629cca Mon Sep 17 00:00:00 2001 +From: Fraser Tweedale +Date: Wed, 1 Feb 2017 16:17:51 +1000 +Subject: [PATCH 2/5] GeneralName: add method to get at inner value + +The 'GeneralNameInterface' interface represents a single X.509 +General Name value. Various types are supported. The 'GeneralName' +class (which also implements 'GeneralNameInterface') is a singleton +container for another 'GeneralNameInterface' value. + +To implement a profile component that copies CN to a SAN dNSName, we +need to examine existing General Names in the SAN extension (if +present), to avoid duplicate values. We can iterate 'GeneralNames', +but if the value is of type 'GeneralName' we need a way to "unwrap" +the value, down to the innermost value which will be of a specific +General Name type. + +Add the 'unwrap' method to 'GeneralName'. + +Part of: https://fedorahosted.org/pki/ticket/1710 + +(cherry picked from commit 225dd099efa7e2f752c3f50157aaec71a9834873) +(cherry picked from commit 52704d6564800c6872d3343c9aa5d6180637f070) +--- + base/util/src/netscape/security/x509/GeneralName.java | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/base/util/src/netscape/security/x509/GeneralName.java b/base/util/src/netscape/security/x509/GeneralName.java +index a90ac7b..55b5bfc 100644 +--- a/base/util/src/netscape/security/x509/GeneralName.java ++++ b/base/util/src/netscape/security/x509/GeneralName.java +@@ -196,4 +196,19 @@ public class GeneralName implements GeneralNameInterface { + constructedForm, (byte) nameType), tmp); + } + } ++ ++ /** ++ * Unwrap this GeneralName until we reach something that is not ++ * a GeneralName. ++ */ ++ public GeneralNameInterface unwrap() { ++ if (this == name) ++ return null; // can't happen, but just in case... ++ ++ if (name instanceof GeneralName) ++ return ((GeneralName) name).unwrap(); ++ else ++ return name; ++ } ++ + } +-- +1.8.3.1 + + +From 6eac5bbccb18fe913c43a0b9ec73707180870bb9 Mon Sep 17 00:00:00 2001 +From: Fraser Tweedale +Date: Wed, 1 Feb 2017 16:25:11 +1000 +Subject: [PATCH 3/5] SubjectAlternativeNameExtension: add GeneralNames + getter/setter + +To implement a profile default that copies CN to SAN dNSName, we +need to read and set the 'GeneralNames' of the extension. This can +be done via the 'get' and 'set' methods but this interface is +awkward and requires the caller to deal with exceptions that aren't +fundamental to the get/set actions. + +Add the 'setGeneralNames' and 'getGeneralNames' methods. + +Part of: https://fedorahosted.org/pki/ticket/1710 + +(cherry picked from commit a67816eebbed2332327fbf391f3e23223ee7690e) +(cherry picked from commit 60f4011c3f4511ac8f86b77940d25b5869204353) +--- + .../security/x509/SubjectAlternativeNameExtension.java | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.java b/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.java +index d96c821..82f87e1 100644 +--- a/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.java ++++ b/base/util/src/netscape/security/x509/SubjectAlternativeNameExtension.java +@@ -199,6 +199,21 @@ public class SubjectAlternativeNameExtension extends Extension + } + + /** ++ * Set the GeneralNames of this extension. ++ */ ++ public void setGeneralNames(GeneralNames names) { ++ clearValue(); ++ this.names = names; ++ } ++ ++ /** ++ * Get the GeneralNames of this extension. ++ */ ++ public GeneralNames getGeneralNames() { ++ return names; ++ } ++ ++ /** + * Get the attribute value. + */ + public Object get(String name) throws IOException { +-- +1.8.3.1 + + +From da8cab2d15d5bd5e82ad8bd9a2ff0f51f7bad343 Mon Sep 17 00:00:00 2001 +From: Fraser Tweedale +Date: Wed, 1 Feb 2017 16:30:50 +1000 +Subject: [PATCH 4/5] X500Name: add method to get all attributes of a given + type + +To implement a profile default that copies the CN to a SAN dNSName, +we need to examine the CN values present in the Subject DN. +Specifically, we want to look at the "most specific" CN value. The +'getCommonName' method returns the "least specific" value in the +name, thus is not suitable. + +Add the 'getAttributesForOid(ObjectIdentifier)' method, which +returns an ordered list of values of the given name attribute type, +from least specific to most specific. + +Part of: https://fedorahosted.org/pki/ticket/1710 + +(cherry picked from commit 979b6a2da433e97c1ada6434b432aa4aabc47ab5) +(cherry picked from commit 4ba23db518ab285d8a0dce8d4ee493f695867ad8) +--- + base/util/src/netscape/security/x509/X500Name.java | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/base/util/src/netscape/security/x509/X500Name.java b/base/util/src/netscape/security/x509/X500Name.java +index 0f75f48..c8627a9 100644 +--- a/base/util/src/netscape/security/x509/X500Name.java ++++ b/base/util/src/netscape/security/x509/X500Name.java +@@ -19,8 +19,10 @@ package netscape.security.x509; + + import java.io.IOException; + import java.security.Principal; ++import java.util.ArrayList; + import java.util.Arrays; + import java.util.Enumeration; ++import java.util.List; + import java.util.Vector; + + import netscape.security.util.DerInputStream; +@@ -451,6 +453,25 @@ public class X500Name implements Principal, GeneralNameInterface { + } + + /** ++ * Return a list of attributes of the given type. ++ * ++ * The "most specific" value comes last. ++ * ++ * If there are no name attributes of the given type, an empty ++ * list is returned. ++ */ ++ public List getAttributesForOid(ObjectIdentifier oid) ++ throws IOException { ++ List xs = new ArrayList<>(); ++ for (int i = 0; i < names.length; i++) { ++ DerValue v = names[i].findAttribute(oid); ++ if (v != null) ++ xs.add(getString(v)); ++ } ++ return xs; ++ } ++ ++ /** + * Returns a Ldap DN String from the X500Name + * using the specified LdapDNStrconverter. + * For example, RFC1779String converter can be passed to convert the +-- +1.8.3.1 + + +From 10799f1af01143ffb27fae06f446bb389c0787e8 Mon Sep 17 00:00:00 2001 +From: Fraser Tweedale +Date: Wed, 1 Feb 2017 16:39:14 +1000 +Subject: [PATCH 5/5] Add profile component that copies CN to SAN dNSName + +Add the 'CommonNameToSANDefault' profile default class. When used +on a profile, this will examine the (most-specific) Common Name in +the Subject DN, and if it looks like a DNS name, will add it to the +Subject Alternative Name extension, creating the extension if it +does not already exist. + +Also add upgrade scriptlet to add the component to registry.cfg in +existing installations. + +Fixes: https://fedorahosted.org/pki/ticket/1710 +(cherry picked from commit 9cb00049ec731cca36de822f6c1e834f7febcb4f) +(cherry picked from commit 10d1db00225caf750ccc3c50b9d6e6b7af3655a8) +--- + base/ca/shared/conf/registry.cfg | 5 +- + .../cms/profile/def/CommonNameToSANDefault.java | 215 +++++++++++++++++++++ + 2 files changed, 219 insertions(+), 1 deletion(-) + create mode 100644 base/server/cms/src/com/netscape/cms/profile/def/CommonNameToSANDefault.java + +diff --git a/base/ca/shared/conf/registry.cfg b/base/ca/shared/conf/registry.cfg +index 0bd7c05..280c713 100644 +--- a/base/ca/shared/conf/registry.cfg ++++ b/base/ca/shared/conf/registry.cfg +@@ -45,7 +45,7 @@ constraintPolicy.renewGracePeriodConstraintImpl.name=Renewal Grace Period Constr + constraintPolicy.uniqueKeyConstraintImpl.class=com.netscape.cms.profile.constraint.UniqueKeyConstraint + constraintPolicy.uniqueKeyConstraintImpl.desc=Unique Public Key Constraint + constraintPolicy.uniqueKeyConstraintImpl.name=Unique Public Key Constraint +-defaultPolicy.ids=noDefaultImpl,genericExtDefaultImpl,autoAssignDefaultImpl,subjectNameDefaultImpl,validityDefaultImpl,randomizedValidityDefaultImpl,caValidityDefaultImpl,subjectKeyIdentifierExtDefaultImpl,authorityKeyIdentifierExtDefaultImpl,basicConstraintsExtDefaultImpl,keyUsageExtDefaultImpl,nsCertTypeExtDefaultImpl,extendedKeyUsageExtDefaultImpl,ocspNoCheckExtDefaultImpl,issuerAltNameExtDefaultImpl,subjectAltNameExtDefaultImpl,userSubjectNameDefaultImpl,signingAlgDefaultImpl,userKeyDefaultImpl,userValidityDefaultImpl,userExtensionDefaultImpl,userSigningAlgDefaultImpl,authTokenSubjectNameDefaultImpl,subjectInfoAccessExtDefaultImpl,authInfoAccessExtDefaultImpl,nscCommentExtDefaultImpl,freshestCRLExtDefaultImpl,crlDistributionPointsExtDefaultImpl,policyConstraintsExtDefaultImpl,policyMappingsExtDefaultImpl,nameConstraintsExtDefaultImpl,certificateVersionDefaultImpl,certificatePoliciesExtDefaultImpl,subjectDirAttributesExtDefaultImpl,privateKeyPeriodExtDefaultImpl,inhibitAnyPolicyExtDefaultImpl,imageDefaultImpl,nsTokenDeviceKeySubjectNameDefaultImpl,nsTokenUserKeySubjectNameDefaultImpl,authzRealmDefaultImpl ++defaultPolicy.ids=noDefaultImpl,genericExtDefaultImpl,autoAssignDefaultImpl,subjectNameDefaultImpl,validityDefaultImpl,randomizedValidityDefaultImpl,caValidityDefaultImpl,subjectKeyIdentifierExtDefaultImpl,authorityKeyIdentifierExtDefaultImpl,basicConstraintsExtDefaultImpl,keyUsageExtDefaultImpl,nsCertTypeExtDefaultImpl,extendedKeyUsageExtDefaultImpl,ocspNoCheckExtDefaultImpl,issuerAltNameExtDefaultImpl,subjectAltNameExtDefaultImpl,userSubjectNameDefaultImpl,signingAlgDefaultImpl,userKeyDefaultImpl,userValidityDefaultImpl,userExtensionDefaultImpl,userSigningAlgDefaultImpl,authTokenSubjectNameDefaultImpl,subjectInfoAccessExtDefaultImpl,authInfoAccessExtDefaultImpl,nscCommentExtDefaultImpl,freshestCRLExtDefaultImpl,crlDistributionPointsExtDefaultImpl,policyConstraintsExtDefaultImpl,policyMappingsExtDefaultImpl,nameConstraintsExtDefaultImpl,certificateVersionDefaultImpl,certificatePoliciesExtDefaultImpl,subjectDirAttributesExtDefaultImpl,privateKeyPeriodExtDefaultImpl,inhibitAnyPolicyExtDefaultImpl,imageDefaultImpl,nsTokenDeviceKeySubjectNameDefaultImpl,nsTokenUserKeySubjectNameDefaultImpl,authzRealmDefaultImpl,commonNameToSANDefaultImpl + defaultPolicy.autoAssignDefaultImpl.class=com.netscape.cms.profile.def.AutoAssignDefault + defaultPolicy.autoAssignDefaultImpl.desc=Auto Request Assignment Default + defaultPolicy.autoAssignDefaultImpl.name=Auto Request Assignment Default +@@ -166,6 +166,9 @@ defaultPolicy.subjectDirAttributesExtDefaultImpl.name=Subject Directory Attribut + defaultPolicy.inhibitAnyPolicyExtDefaultImpl.class=com.netscape.cms.profile.def.InhibitAnyPolicyExtDefault + defaultPolicy.inhibitAnyPolicyExtDefaultImpl.desc=Inhibit Any-Policy Extension Default + defaultPolicy.inhibitAnyPolicyExtDefaultImpl.name=Inhibit Any-Policy Extension Default ++defaultPolicy.commonNameToSANDefaultImpl.class=com.netscape.cms.profile.def.CommonNameToSANDefault ++defaultPolicy.commonNameToSANDefaultImpl.desc=Copy Common Name to Subject Alternative Name ++defaultPolicy.commonNameToSANDefaultImpl.name=Copy Common Name to Subject Alternative Name + profile.ids=caEnrollImpl,caCACertEnrollImpl,caServerCertEnrollImpl,caUserCertEnrollImpl + profile.caEnrollImpl.class=com.netscape.cms.profile.common.CAEnrollProfile + profile.caEnrollImpl.desc=Certificate Authority Generic Certificate Enrollment Profile +diff --git a/base/server/cms/src/com/netscape/cms/profile/def/CommonNameToSANDefault.java b/base/server/cms/src/com/netscape/cms/profile/def/CommonNameToSANDefault.java +new file mode 100644 +index 0000000..33828d1 +--- /dev/null ++++ b/base/server/cms/src/com/netscape/cms/profile/def/CommonNameToSANDefault.java +@@ -0,0 +1,215 @@ ++// --- 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) 2017 Red Hat, Inc. ++// All rights reserved. ++// --- END COPYRIGHT BLOCK --- ++ ++package com.netscape.cms.profile.def; ++ ++import java.io.IOException; ++import java.security.cert.CertificateException; ++import java.util.List; ++import java.util.Locale; ++ ++import netscape.security.x509.CertificateSubjectName; ++import netscape.security.x509.DNSName; ++import netscape.security.x509.GeneralName; ++import netscape.security.x509.GeneralNameInterface; ++import netscape.security.x509.GeneralNames; ++import netscape.security.x509.PKIXExtensions; ++import netscape.security.x509.SubjectAlternativeNameExtension; ++import netscape.security.x509.X500Name; ++import netscape.security.x509.X509CertInfo; ++ ++import com.netscape.certsrv.apps.CMS; ++import com.netscape.certsrv.profile.EProfileException; ++import com.netscape.certsrv.property.IDescriptor; ++import com.netscape.certsrv.request.IRequest; ++ ++/** ++ * This plugin will examine the most specific CN in the Subject DN, ++ * and if it looks like a DNS name, will add it to the SAN extension. ++ * ++ * It will create the SAN extension if necessary. ++ * ++ * If there is already a SAN dnsName value that matches ++ * (case-insensitively) the CN, it will not add the name. ++ * ++ * If there is no CN in the subject DN, does nothing. ++ * ++ * If the most specific CN does not look like a DNS name, does ++ * nothing. ++ * ++ * This profile component should be configured to execute after ++ * other profile components that set or modify the Subject DN or the ++ * SAN extension. ++ */ ++public class CommonNameToSANDefault extends EnrollExtDefault { ++ ++ private static final String LOG_PREFIX = "CommonNameToSANDefault: "; ++ ++ public void populate(IRequest _req, X509CertInfo info) ++ throws EProfileException { ++ // examine the Subject DN ++ CertificateSubjectName subjectName; ++ try { ++ subjectName = (CertificateSubjectName) info.get(X509CertInfo.SUBJECT); ++ } catch (CertificateException | IOException e) { ++ CMS.debug(LOG_PREFIX + "failed to read Subject DN: " + e); ++ return; ++ } ++ X500Name sdn; ++ try { ++ sdn = (X500Name) subjectName.get(CertificateSubjectName.DN_NAME); ++ } catch (IOException e) { ++ CMS.debug(LOG_PREFIX + "failed to retrieve SDN X500Name: " + e); ++ return; ++ } ++ List cns; ++ try { ++ cns = sdn.getAttributesForOid(X500Name.commonName_oid); ++ } catch (IOException e) { ++ // Couldn't read the CN for some reason. ++ // Not a likely scenario so just log and return. ++ CMS.debug(LOG_PREFIX + "failed to decode CN: " + e); ++ return; ++ } ++ if (cns.size() < 1) { ++ CMS.debug(LOG_PREFIX + "No CN in Subject DN; done"); ++ return; // no Common Name; can't do anything ++ } ++ ++ String cn = cns.get(cns.size() - 1); // "most specific" CN is at end ++ ++ CMS.debug(LOG_PREFIX + "Examining CN: " + cn); ++ ++ if (!isValidDNSName(cn)) { ++ CMS.debug(LOG_PREFIX + "CN is not a DNS name; done"); ++ return; // CN does not look like a DNS name ++ } ++ ++ SubjectAlternativeNameExtension san = (SubjectAlternativeNameExtension) ++ getExtension(PKIXExtensions.SubjectAlternativeName_Id.toString(), info); ++ ++ if (san != null) { ++ // check for existing name matching CN ++ GeneralNames gns = san.getGeneralNames(); ++ for (GeneralNameInterface gn : gns) { ++ if (gn instanceof GeneralName) ++ gn = ((GeneralName) gn).unwrap(); ++ if (gn instanceof DNSName) { ++ String dnsName = ((DNSName) gn).getValue(); ++ if (cn.equalsIgnoreCase(dnsName)) { ++ CMS.debug(LOG_PREFIX ++ + "CN already has corresponding SAN dNSName; done"); ++ return; // CN is already in SAN ++ } ++ } ++ } ++ gns.add(new DNSName(cn)); // add CN to SAN ++ ++ // reset extension value (encoded value may have been cached) ++ san.setGeneralNames(gns); ++ CMS.debug(LOG_PREFIX + "added CN to SAN; done"); ++ } else { ++ GeneralNames gns = new GeneralNames(); ++ gns.add(new DNSName(cn)); ++ try { ++ san = new SubjectAlternativeNameExtension(gns); ++ addExtension( ++ PKIXExtensions.SubjectAlternativeName_Id.toString(), san, info); ++ } catch (IOException e) { ++ CMS.debug(LOG_PREFIX + "failed to construct SAN ext: " + e); ++ return; ++ } ++ CMS.debug(LOG_PREFIX + "added SAN extension containing CN; done"); ++ } ++ } ++ ++ public String getText(Locale locale) { ++ return "This default add the Subject DN Common Name to the Subject " ++ + "Alternative Name extension, if it looks like a DNS name."; ++ } ++ ++ public IDescriptor getValueDescriptor(Locale locale, String name) { ++ return null; ++ } ++ ++ public String getValue(String name, Locale locale, X509CertInfo info) { ++ return null; ++ } ++ ++ public void setValue( ++ String name, Locale locale, X509CertInfo info, String value) { ++ } ++ ++ /** Validate DNS name syntax per Section 3.5 of RFC 1034 ++ * and Section 2.1 of RFC 1123, and the additional rules ++ * of RFC 5280 Section 4.2.1.6. ++ * ++ * Further to those rules, we also ignore CNs that are valid ++ * DNS names but which only have a single part (e.g. TLDs or ++ * host short names). ++ */ ++ public static boolean isValidDNSName(String s) { ++ if (s == null) ++ return false; ++ ++ if (s.length() < 1 || s.length() > 255) ++ return false; ++ ++ String[] parts = s.split("\\."); ++ ++ if (parts.length < 2) ++ return false; ++ ++ for (int i = 0; i < parts.length; i++) { ++ char[] cs = parts[i].toCharArray(); ++ ++ if (cs.length < 1 || cs.length > 63) ++ return false; ++ ++ if (!isLetter(cs[0])) ++ return false; ++ ++ if (!isLetDig(cs[cs.length - 1])) ++ return false; ++ ++ for (int j = 0; j < cs.length; j++) { ++ if (!isLetDigHyp(cs[j])) ++ return false; ++ } ++ } ++ ++ return true; ++ } ++ ++ public static boolean isLetter(char c) { ++ return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'; ++ } ++ ++ public static boolean isDigit(char c) { ++ return c >= '0' && c <= '9'; ++ } ++ ++ public static boolean isLetDig(char c) { ++ return isLetter(c) || isDigit(c); ++ } ++ ++ public static boolean isLetDigHyp(char c) { ++ return isLetDig(c) || c == '-'; ++ } ++ ++} +-- +1.8.3.1 + diff --git a/SPECS/pki-core.spec b/SPECS/pki-core.spec index d2203e0..fd30e7f 100644 --- a/SPECS/pki-core.spec +++ b/SPECS/pki-core.spec @@ -65,8 +65,8 @@ Name: pki-core Version: 10.3.3 -#Release: 17%{?dist} -Release: 17.el7_3 +#Release: 18%{?dist} +Release: 18.el7_3 Summary: Certificate System - PKI Core Components URL: http://pki.fedoraproject.org/ License: GPLv2 @@ -299,6 +299,11 @@ Patch39: pki-core-slf4j-api.patch Patch40: pki-core-javadoc-special-characters.patch ## RHCS 9.1 (async) Batch Update 3 #Patch41: pki-core-reset-cert-status-after-successful-unrevoke.patch +####################### +## pki-core-10.3.3-18 +####################### +## RHEL 7.3.z Batch Update 4 +Patch42: pki-core-add-profile-component-that-copies-CN-to-SAN.patch # Obtain version phase number (e. g. - used by "alpha", "beta", etc.) # @@ -954,6 +959,7 @@ This package is a part of the PKI Core used by the Certificate System. %patch39 -p1 %patch40 -p1 #%patch41 -p1 +%patch42 -p1 %clean %{__rm} -rf %{buildroot} @@ -1459,6 +1465,11 @@ systemctl daemon-reload %endif # %{with server} %changelog +* Mon Mar 6 2017 Dogtag Team 10.3.3-18 +- ## RHEL 7.3.z Batch Update 4 +- Bugzilla Bug #1429492 - Add profile component that copies CN to SAN + (ftweedal) + * Mon Jan 30 2017 Dogtag Team 10.3.3-17 - ## RHCS 9.1.z Batch Update 3 - Bugzilla Bug #1391207 - Automatic recovery of encryption cert - CA and TPS