diff --git a/closed/src/java.base/share/classes/openj9/internal/security/RestrictedSecurity.java b/closed/src/java.base/share/classes/openj9/internal/security/RestrictedSecurity.java index d6d41f53b4..488b6e0927 100644 --- a/closed/src/java.base/share/classes/openj9/internal/security/RestrictedSecurity.java +++ b/closed/src/java.base/share/classes/openj9/internal/security/RestrictedSecurity.java @@ -23,7 +23,10 @@ */ package openj9.internal.security; +import java.nio.charset.StandardCharsets; import java.security.AccessController; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.PrivilegedAction; import java.security.Provider.Service; import java.time.LocalDate; @@ -42,6 +45,8 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; import sun.security.util.Debug; @@ -71,6 +76,8 @@ public final class RestrictedSecurity { private static String userSecurityID; + private static ProfileParser profileParser; + private static RestrictedSecurityProperties restricts; private static final Set unmodifiableProperties = new HashSet<>(); @@ -164,6 +171,20 @@ private RestrictedSecurity() { super(); } + /** + * Check loaded profiles' hash values. + * + * In order to avoid unintentional changes in profiles and incentivize + * extending profiles, instead of altering them, a digest of the profile + * is calculated and compared to the expected value. + */ + public static void checkHashValues() { + if (profileParser != null) { + profileParser.checkHashValues(); + profileParser = null; + } + } + /** * Check if restricted security mode is enabled. * @@ -290,7 +311,7 @@ public static boolean isProviderAllowed(Class providerClazz) { private static void getProfileID(Properties props) { String potentialProfileID = "RestrictedSecurity." + selectedProfile; - if (selectedProfile.indexOf(".") != -1) { + if (selectedProfile.indexOf('.') != -1) { /* The default profile is used, or the user specified the * full . */ @@ -360,8 +381,8 @@ private static void checkIfKnownProfileSupported() { } } - private static void checkFIPSCompatibility(Properties props) { - boolean isFIPSProfile = Boolean.parseBoolean(props.getProperty(profileID + ".desc.fips")); + private static void checkFIPSCompatibility() { + boolean isFIPSProfile = restricts.descIsFIPS; if (isFIPSProfile) { if (debug != null) { debug.println("RestrictedSecurity profile " + profileID @@ -443,13 +464,9 @@ public static boolean configure(Properties props) { getProfileID(props); checkIfKnownProfileSupported(); - // If user enabled FIPS, check whether chosen profile is applicable. - if (userEnabledFIPS) { - checkFIPSCompatibility(props); - } - // Initialize restricted security properties from java.security file. - restricts = new RestrictedSecurityProperties(profileID, props); + profileParser = new ProfileParser(profileID, props); + restricts = profileParser.getProperties(); // Restricted security properties checks. restrictsCheck(); @@ -538,22 +555,16 @@ private static void setProperties(Properties props) { String jdkPropsName = entry.getKey(); String propsNewValue = entry.getValue(); - String propsOldValue = props.getProperty(jdkPropsName); - if (isNullOrBlank(propsOldValue)) { - propsOldValue = ""; - } - if ((propsNewValue != null) && userEnabledFIPS && !allowSetProperties) { // Add to set of properties set by the active profile. unmodifiableProperties.add(jdkPropsName); } if (!isNullOrBlank(propsNewValue)) { - String values = isNullOrBlank(propsOldValue) ? propsNewValue : (propsOldValue + ", " + propsNewValue); - props.setProperty(jdkPropsName, values); + props.setProperty(jdkPropsName, propsNewValue); if (debug != null) { - debug.println("Added restricted security properties, with property: " + jdkPropsName + " value: " - + values); + debug.println("Added restricted security properties, with property: " + + jdkPropsName + " value: " + propsNewValue); } } } @@ -589,6 +600,11 @@ private static void restrictsCheck() { || isNullOrBlank(restricts.jdkSecureRandomAlgorithm)) { printStackTraceAndExit("Restricted security mode secure random is missing."); } + + // If user enabled FIPS, check whether chosen profile is applicable. + if (userEnabledFIPS) { + checkFIPSCompatibility(); + } } /** @@ -599,12 +615,15 @@ private static void restrictsCheck() { */ private static boolean isPolicySunset(String descSunsetDate) { boolean isSunset = false; - try { - isSunset = LocalDate.parse(descSunsetDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")) - .isBefore(LocalDate.now()); - } catch (DateTimeParseException except) { - printStackTraceAndExit( - "Restricted security policy sunset date is incorrect, the correct format is yyyy-MM-dd."); + // Only check if a sunset date is specified in the profile. + if (!isNullOrBlank(descSunsetDate)) { + try { + isSunset = LocalDate.parse(descSunsetDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")) + .isBefore(LocalDate.now()); + } catch (DateTimeParseException except) { + printStackTraceAndExit( + "Restricted security policy sunset date is incorrect, the correct format is yyyy-MM-dd."); + } } if (debug != null) { @@ -632,34 +651,66 @@ private static void printStackTraceAndExit(String message) { printStackTraceAndExit(new RuntimeException(message)); } + /** + * Check if the input string is asterisk (*). + * + * @param string input string for checking + * @return true if the input string is asterisk + */ + private static boolean isAsterisk(String string) { + return "*".equals(string); + } + + /** + * Get the provider name defined in provider construction method. + * + * @param providerName provider name or provider with packages + * @return provider name defined in provider construction method + */ + private static String getProvidersSimpleName(String providerName) { + if (providerName.equals("com.sun.security.sasl.Provider")) { + // The main class for the SunSASL provider is com.sun.security.sasl.Provider. + return "SunSASL"; + } else { + // Remove the provider's class package names if present. + int pos = providerName.lastIndexOf('.'); + if (pos >= 0) { + providerName = providerName.substring(pos + 1); + } + // Provider without package names. + return providerName; + } + } + /** * This class is used to save and operate on restricted security * properties which are loaded from the java.security file. */ private static final class RestrictedSecurityProperties { + private final String profileID; - private String descName; - private boolean descIsDefault; - private boolean descIsFIPS; - private String descNumber; - private String descPolicy; - private String descSunsetDate; + private final String descName; + private final boolean descIsDefault; + private final boolean descIsFIPS; + private final String descNumber; + private final String descPolicy; + private final String descSunsetDate; // Security properties. - private String jdkTlsDisabledNamedCurves; - private String jdkTlsDisabledAlgorithms; - private String jdkTlsEphemeralDHKeySize; - private String jdkTlsLegacyAlgorithms; - private String jdkCertpathDisabledAlgorithms; - private String jdkSecurityLegacyAlgorithms; - private String keyStoreType; - private String keyStore; + private final String jdkTlsDisabledNamedCurves; + private final String jdkTlsDisabledAlgorithms; + private final String jdkTlsEphemeralDHKeySize; + private final String jdkTlsLegacyAlgorithms; + private final String jdkCertpathDisabledAlgorithms; + private final String jdkSecurityLegacyAlgorithms; + private final String keyStoreType; + private final String keyStore; // For SecureRandom. - String jdkSecureRandomProvider; - String jdkSecureRandomAlgorithm; + final String jdkSecureRandomProvider; + final String jdkSecureRandomAlgorithm; - String jdkFipsMode; + final String jdkFipsMode; // Provider with argument (provider name + optional argument). private final List providers; @@ -668,265 +719,49 @@ private static final class RestrictedSecurityProperties { // The map is keyed by provider name. private final Map providerConstraints; - private final String profileID; - - // The java.security properties. - private final Properties securityProps; - - /** - * - * @param id the restricted security custom profile ID - * @param props the java.security properties - * @param trace the user security trace - * @param audit the user security audit - * @param help the user security help - */ - private RestrictedSecurityProperties(String id, Properties props) { - Objects.requireNonNull(props); - - profileID = id; - securityProps = props; - - providers = new ArrayList<>(); - providersSimpleName = new ArrayList<>(); - providerConstraints = new HashMap<>(); - - // Initialize the properties. - init(); - } - - /** - * Initialize restricted security properties. - */ - private void init() { - if (debug != null) { - debug.println("Initializing restricted security mode."); - } - - try { - // Load restricted security providers from java.security properties. - initProviders(); - // Load restricted security properties from java.security properties. - initProperties(); - // Load restricted security provider constraints from java.security properties. - initConstraints(); - } catch (Exception e) { - if (debug != null) { - debug.println("Unable to initialize restricted security mode."); - } - printStackTraceAndExit(e); - } + private RestrictedSecurityProperties(String profileID, ProfileParser parser) { + this.profileID = profileID; + + this.descName = parser.getProperty("descName"); + this.descIsDefault = parser.descIsDefault; + this.descIsFIPS = parser.descIsFIPS; + this.descNumber = parser.getProperty("descNumber"); + + this.descPolicy = parser.getProperty("descPolicy"); + this.descSunsetDate = parser.getProperty("descSunsetDate"); + + // Security properties. + this.jdkTlsDisabledNamedCurves = parser.getProperty("jdkTlsDisabledNamedCurves"); + this.jdkTlsDisabledAlgorithms = parser.getProperty("jdkTlsDisabledAlgorithms"); + this.jdkTlsEphemeralDHKeySize = parser.getProperty("jdkTlsEphemeralDHKeySize"); + this.jdkTlsLegacyAlgorithms = parser.getProperty("jdkTlsLegacyAlgorithms"); + this.jdkCertpathDisabledAlgorithms = parser.getProperty("jdkCertpathDisabledAlgorithms"); + this.jdkSecurityLegacyAlgorithms = parser.getProperty("jdkSecurityLegacyAlgorithms"); + this.keyStoreType = parser.getProperty("keyStoreType"); + this.keyStore = parser.getProperty("keyStore"); + + // For SecureRandom. + this.jdkSecureRandomProvider = parser.getProperty("jdkSecureRandomProvider"); + this.jdkSecureRandomAlgorithm = parser.getProperty("jdkSecureRandomAlgorithm"); + + this.jdkFipsMode = parser.getProperty("jdkFipsMode"); + + this.providers = new ArrayList<>(parser.providers); + this.providersSimpleName = new ArrayList<>(parser.providersSimpleName); + this.providerConstraints = parser.providerConstraints + .entrySet() + .stream() + .collect(Collectors.toMap( + e -> e.getKey(), + e -> e.getValue().toArray(new Constraint[0]) + )); if (debug != null) { - debug.println("Initialization of restricted security mode completed."); - - // Print all available restricted security profiles. - listAvailableProfiles(); - // Print information of utilized security profile. listUsedProfile(); } } - /** - * Load restricted security provider. - */ - private void initProviders() { - if (debug != null) { - debug.println("\tLoading providers of restricted security profile."); - } - - for (int pNum = 1;; ++pNum) { - String providerInfo = securityProps - .getProperty(profileID + ".jce.provider." + pNum); - - if ((providerInfo == null) || providerInfo.trim().isEmpty()) { - break; - } - - if (!areBracketsBalanced(providerInfo)) { - printStackTraceAndExit("Provider format is incorrect: " + providerInfo); - } - - int pos = providerInfo.indexOf('['); - String providerName = (pos < 0) ? providerInfo.trim() : providerInfo.substring(0, pos).trim(); - // Provider with argument (provider name + optional argument). - providers.add(pNum - 1, providerName); - - // Remove the provider's optional arguments if there are. - pos = providerName.indexOf(' '); - if (pos >= 0) { - providerName = providerName.substring(0, pos); - } - providerName = providerName.trim(); - - // Remove argument, e.g. -NSS-FIPS, if present. - pos = providerName.indexOf('-'); - if (pos >= 0) { - providerName = providerName.substring(0, pos); - } - - // Provider name defined in provider construction method. - providerName = getProvidersSimpleName(providerName); - providersSimpleName.add(pNum - 1, providerName); - } - - if (providers.isEmpty()) { - printStackTraceAndExit( - "No providers are specified as part of the Restricted Security profile."); - } - - if (debug != null) { - debug.println("\tProviders of restricted security profile successfully loaded."); - } - } - - /** - * Load restricted security properties. - */ - private void initProperties() { - if (debug != null) { - debug.println("\tLoading properties of restricted security profile."); - } - - descName = parseProperty(securityProps.getProperty(profileID + ".desc.name")); - descIsDefault = Boolean.parseBoolean(parseProperty(securityProps.getProperty(profileID + ".desc.default"))); - descIsFIPS = Boolean.parseBoolean(parseProperty(securityProps.getProperty(profileID + ".desc.fips"))); - descNumber = parseProperty(securityProps.getProperty(profileID + ".desc.number")); - descPolicy = parseProperty(securityProps.getProperty(profileID + ".desc.policy")); - descSunsetDate = parseProperty(securityProps.getProperty(profileID + ".desc.sunsetDate")); - - jdkTlsDisabledNamedCurves = parseProperty( - securityProps.getProperty(profileID + ".tls.disabledNamedCurves")); - jdkTlsDisabledAlgorithms = parseProperty( - securityProps.getProperty(profileID + ".tls.disabledAlgorithms")); - jdkTlsEphemeralDHKeySize = parseProperty( - securityProps.getProperty(profileID + ".tls.ephemeralDHKeySize")); - jdkTlsLegacyAlgorithms = parseProperty( - securityProps.getProperty(profileID + ".tls.legacyAlgorithms")); - jdkCertpathDisabledAlgorithms = parseProperty( - securityProps.getProperty(profileID + ".jce.certpath.disabledAlgorithms")); - jdkSecurityLegacyAlgorithms = parseProperty( - securityProps.getProperty(profileID + ".jce.legacyAlgorithms")); - keyStoreType = parseProperty( - securityProps.getProperty(profileID + ".keystore.type")); - keyStore = parseProperty( - securityProps.getProperty(profileID + ".javax.net.ssl.keyStore")); - - jdkSecureRandomProvider = parseProperty( - securityProps.getProperty(profileID + ".securerandom.provider")); - jdkSecureRandomAlgorithm = parseProperty( - securityProps.getProperty(profileID + ".securerandom.algorithm")); - jdkFipsMode = parseProperty( - securityProps.getProperty(profileID + ".fips.mode")); - - if (debug != null) { - debug.println("\tProperties of restricted security profile successfully loaded."); - } - } - - /** - * Load security constraints with type, algorithm, attributes. - * - * Example: - * RestrictedSecurity1.jce.provider.1 = SUN [{CertPathBuilder, PKIX, *}, - * {Policy, JavaPolicy, *}, {CertPathValidator, *, *}]. - */ - private void initConstraints() { - if (debug != null) { - debug.println("\tLoading constraints of restricted security profile."); - } - - for (int pNum = 1; pNum <= providersSimpleName.size(); pNum++) { - String providerName = providersSimpleName.get(pNum - 1); - String providerInfo = securityProps - .getProperty(profileID + ".jce.provider." + pNum); - - if (debug != null) { - debug.println("\t\tLoading constraints for security provider: " + providerName); - } - - // Check if the provider has constraints. - if (providerInfo.indexOf('[') < 0) { - if (debug != null) { - debug.println("\t\t\tNo constraints for security provider: " + providerName); - } - providerConstraints.put(providerName, new Constraint[0]); - continue; - } - - // Remove the whitespaces in the format separator if there are. - providerInfo = providerInfo.trim() - .replaceAll("\\[\\s+\\{", "[{") - .replaceAll("\\}\\s+\\]", "}]") - .replaceAll("\\}\\s*,\\s*\\{", "},{"); - - int startIndex = providerInfo.lastIndexOf("[{"); - int endIndex = providerInfo.indexOf("}]"); - - // Provider with constraints. - if ((startIndex > 0) && (endIndex > startIndex)) { - String[] constrArray = providerInfo - .substring(startIndex + 2, endIndex).split("\\},\\{"); - - if (constrArray.length <= 0) { - printStackTraceAndExit("Constraint format is incorrect: " + providerInfo); - } - - // Constraint object array. - // For each constraint type, algorithm and attributes. - Constraint[] constraints = new Constraint[constrArray.length]; - - int cNum = 0; - for (String constr : constrArray) { - String[] input = constr.split(","); - - // Each constraint must includes 3 fields(type, algorithm, attributes). - if (input.length != 3) { - printStackTraceAndExit("Constraint format is incorrect: " + providerInfo); - } - - String inType = input[0].trim(); - String inAlgorithm = input[1].trim(); - String inAttributes = input[2].trim(); - - // Each attribute must includes 2 fields (key and value) or *. - if (!isAsterisk(inAttributes)) { - String[] attributeArray = inAttributes.split(":"); - for (String attribute : attributeArray) { - String[] in = attribute.split("=", 2); - if (in.length != 2) { - printStackTraceAndExit( - "Constraint attributes format is incorrect: " + providerInfo); - } - } - } - - Constraint constraint = new Constraint(inType, inAlgorithm, inAttributes); - - if (debug != null) { - debug.println("\t\t\tConstraint specified for security provider: " + providerName); - debug.println("\t\t\t\twith type: " + inType); - debug.println("\t\t\t\tfor algorithm: " + inAlgorithm); - debug.println("\t\t\t\twith attributes: " + inAttributes); - } - constraints[cNum] = constraint; - cNum++; - } - providerConstraints.put(providerName, constraints); - if (debug != null) { - debug.println("\t\tSuccessfully loaded constraints for security provider: " + providerName); - } - } else { - printStackTraceAndExit("Constraint format is incorrect: " + providerInfo); - } - } - - if (debug != null) { - debug.println("\tAll constraints of restricted security profile successfully loaded."); - } - } - /** * Check if the Service is allowed in restricted security mode. * @@ -936,6 +771,10 @@ private void initConstraints() { boolean isRestrictedServiceAllowed(Service service) { String providerName = service.getProvider().getName(); + if (debug != null) { + debug.println("Checking service " + service.toString() + " offered by provider " + providerName + "."); + } + // Provider with argument, remove argument. // e.g. SunPKCS11-NSS-FIPS, remove argument -NSS-FIPS. int pos = providerName.indexOf('-'); @@ -945,9 +784,16 @@ boolean isRestrictedServiceAllowed(Service service) { if (constraints == null) { // Disallow unknown providers. + if (debug != null) { + debug.println("Security constraints check." + + " Disallow unknown provider: " + providerName); + } return false; } else if (constraints.length == 0) { // Allow this provider with no constraints. + if (debug != null) { + debug.println("No constraints for provider " + providerName + "."); + } return true; } @@ -955,27 +801,42 @@ boolean isRestrictedServiceAllowed(Service service) { String type = service.getType(); String algorithm = service.getAlgorithm(); + if (debug != null) { + debug.println("Security constraints check of provider."); + } for (Constraint constraint : constraints) { String cType = constraint.type; String cAlgorithm = constraint.algorithm; String cAttribute = constraint.attributes; + if (debug != null) { + debug.println("Checking provider constraint:" + + "\n\tService type: " + cType + + "\n\tAlgorithm: " + cAlgorithm + + "\n\tAttributes: " + cAttribute); + } if (!isAsterisk(cType) && !type.equals(cType)) { // The constraint doesn't apply to the service type. + if (debug != null) { + debug.println("The constraint doesn't apply to the service type."); + } continue; } if (!isAsterisk(cAlgorithm) && !algorithm.equalsIgnoreCase(cAlgorithm)) { - // The constraint doesn't apply to the service algorith. + // The constraint doesn't apply to the service algorithm. + if (debug != null) { + debug.println("The constraint doesn't apply to the service algorithm."); + } continue; } // For type and algorithm match, and attribute is *. if (isAsterisk(cAttribute)) { if (debug != null) { - debug.println("Security constraints check." - + " Service type: " + type - + " Algorithm: " + algorithm - + " is allowed in provider " + providerName); + debug.println("The following service:" + + "\n\tService type: " + type + + "\n\tAlgorithm: " + algorithm + + "\nis allowed in provider: " + providerName); } return true; } @@ -991,36 +852,47 @@ boolean isRestrictedServiceAllowed(Service service) { String cName = input[0].trim(); String cValue = input[1].trim(); String sValue = service.getAttribute(cName); + if (debug != null) { + debug.println("Checking specific attribute with:" + + "\n\tName: " + cName + + "\n\tValue: " + cValue + + "\nagainst the service attribute value: " + sValue); + } if ((sValue == null) || !cValue.equalsIgnoreCase(sValue)) { // If any attribute doesn't match, return service is not allowed. if (debug != null) { - debug.println( - "Security constraints check." - + " Service type: " + type - + " Algorithm: " + algorithm - + " Attribute: " + cAttribute - + " is NOT allowed in provider: " + providerName); + debug.println("Attributes don't match!"); + debug.println("The following service:" + + "\n\tService type: " + type + + "\n\tAlgorithm: " + algorithm + + "\n\tAttribute: " + cAttribute + + "\nis NOT allowed in provider: " + providerName); } return false; } + if (debug != null) { + debug.println("Attributes match!"); + } } if (debug != null) { - debug.println( - "Security constraints check." - + " Service type: " + type - + " Algorithm: " + algorithm - + " Attribute: " + cAttribute - + " is allowed in provider: " + providerName); + debug.println("All attributes matched!"); + debug.println("The following service:" + + "\n\tService type: " + type + + "\n\tAlgorithm: " + algorithm + + "\n\tAttribute: " + cAttribute + + "\nis allowed in provider: " + providerName); } return true; } + + // No match for any constraint, return NOT allowed. if (debug != null) { - debug.println("Security constraints check." - + " Service type: " + type - + " Algorithm: " + algorithm - + " is NOT allowed in provider " + providerName); + debug.println("Could not find a constraint to match."); + debug.println("The following service:" + + "\n\tService type: " + type + + "\n\tAlgorithm: " + algorithm + + "\nis NOT allowed in provider: " + providerName); } - // No match for any constraint, return NOT allowed. return false; } @@ -1035,7 +907,7 @@ boolean isRestrictedProviderAllowed(String providerName) { debug.println("Checking the provider " + providerName + " in restricted security mode."); } - // Remove argument, e.g. -NSS-FIPS, if there is. + // Remove argument, e.g. -NSS-FIPS, if present. int pos = providerName.indexOf('-'); if (pos >= 0) { providerName = providerName.substring(0, pos); @@ -1068,23 +940,605 @@ boolean isRestrictedProviderAllowed(String providerName) { } /** - * Get the provider name defined in provider construction method. + * List the RestrictedSecurity profile currently used. + */ + private void listUsedProfile() { + System.out.println(); + System.out.println("Utilized Restricted Security Profile Info:"); + System.out.println("=========================================="); + System.out.println("The Restricted Security profile used is: " + profileID); + System.out.println(); + System.out.println(profileID + " Profile Info:"); + System.out.println("=========================================="); + printProperty(profileID + ".desc.name: ", descName); + printProperty(profileID + ".desc.default: ", "" + descIsDefault); + printProperty(profileID + ".desc.fips: ", "" + descIsFIPS); + printProperty(profileID + ".fips.mode: ", jdkFipsMode); + printProperty(profileID + ".desc.number: ", descNumber); + printProperty(profileID + ".desc.policy: ", descPolicy); + printProperty(profileID + ".desc.sunsetDate: ", descSunsetDate); + System.out.println(); + + // List providers. + System.out.println(profileID + " Profile Providers:"); + System.out.println("==============================================="); + for (int providerPosition = 0; providerPosition < providers.size(); providerPosition++) { + printProperty(profileID + ".jce.provider." + (providerPosition + 1) + ": ", + providers.get(providerPosition)); + String providerSimpleName = providersSimpleName.get(providerPosition); + for (Constraint providerConstraint : providerConstraints.get(providerSimpleName)) { + System.out.println("\t" + providerConstraint.toString()); + } + } + System.out.println(); + + // List profile restrictions. + System.out.println(profileID + " Profile Restrictions:"); + System.out.println("=================================================="); + printProperty(profileID + ".tls.disabledNamedCurves: ", jdkTlsDisabledNamedCurves); + printProperty(profileID + ".tls.disabledAlgorithms: ", jdkTlsDisabledAlgorithms); + printProperty(profileID + ".tls.ephemeralDHKeySize: ", jdkTlsEphemeralDHKeySize); + printProperty(profileID + ".tls.legacyAlgorithms: ", jdkTlsLegacyAlgorithms); + printProperty(profileID + ".jce.certpath.disabledAlgorithms: ", jdkCertpathDisabledAlgorithms); + printProperty(profileID + ".jce.legacyAlgorithms: ", jdkSecurityLegacyAlgorithms); + System.out.println(); + + printProperty(profileID + ".keystore.type: ", keyStoreType); + printProperty(profileID + ".javax.net.ssl.keyStore: ", keyStore); + printProperty(profileID + ".securerandom.provider: ", jdkSecureRandomProvider); + printProperty(profileID + ".securerandom.algorithm: ", jdkSecureRandomAlgorithm); + System.out.println(); + } + + private static void printProperty(String name, String value) { + if (value != null) { + String valueToPrint = (value.isEmpty()) ? "EMPTY" : value; + System.out.println(name + valueToPrint); + } else if (debug != null) { + debug.println("Nothing to print. Value of property " + name + " is null."); + } + } + } + + private static final class ProfileParser { + // Properties specified through the profile. + private final Map profileProperties; + private boolean descIsDefault; + private boolean descIsFIPS; + + // Provider with argument (provider name + optional argument). + private final List providers; + // Provider without argument. + private final List providersSimpleName; + // The map is keyed by provider name. + private final Map> providerConstraints; + + private final String profileID; + + private final Map profilesHashes; + private final Map> profilesInfo; + + private final Set parsedProfiles; + + // The java.security properties. + private final Properties securityProps; + + /** * - * @param providerName provider name or provider with packages - * @return provider name defined in provider construction method + * @param id the restricted security custom profile ID + * @param props the java.security properties + */ + private ProfileParser(String id, Properties props) { + Objects.requireNonNull(props); + + profileID = id; + securityProps = props; + + profileProperties = new HashMap<>(); + + providers = new ArrayList<>(); + providersSimpleName = new ArrayList<>(); + providerConstraints = new HashMap<>(); + + profilesHashes = new HashMap<>(); + profilesInfo = new HashMap<>(); + + parsedProfiles = new HashSet<>(); + + // Initialize the properties. + init(profileID); + } + + private RestrictedSecurityProperties getProperties() { + return new RestrictedSecurityProperties(this.profileID, this); + } + + private boolean isFIPS1402Profile(String profileID) { + return "140-2".equals(securityProps.getProperty(profileID + ".fips.mode")); + } + + /** + * Initialize restricted security properties. + */ + private void init(String profileID) { + if (debug != null) { + debug.println("Initializing restricted security properties for '" + profileID + "'."); + } + + if (!parsedProfiles.add(profileID)) { + printStackTraceAndExit(profileID + " has already been parsed. Potential infinite recursion."); + } + + String potentialExtendsProfileID = parseProperty(securityProps.getProperty(profileID + ".extends")); + if (potentialExtendsProfileID != null) { // If profile extends another profile. + if (debug != null) { + debug.println("\t'" + profileID + "' extends '" + potentialExtendsProfileID + "'."); + } + + // Check if extended profile exists. + String extendsProfileID = null; + if (potentialExtendsProfileID.indexOf('.') != potentialExtendsProfileID.lastIndexOf('.')) { + // Extended profile id has at least 2 dots (meaning it's a full profile id). + int prefixLength = potentialExtendsProfileID.length(); + for (Object keyObject : securityProps.keySet()) { + if (keyObject instanceof String) { + String key = (String) keyObject; + if (key.startsWith(potentialExtendsProfileID)) { + String suffix = key.substring(prefixLength); + if (suffix.startsWith(".desc") + || suffix.startsWith(".fips") + || suffix.startsWith(".javax") + || suffix.startsWith(".jce") + || suffix.startsWith(".securerandom") + || suffix.startsWith(".tls") + ) { + /* If even one security property is found for this profile id, + * then it is a valid one and there is no need to check more + * properties. + */ + extendsProfileID = potentialExtendsProfileID; + break; + } + } + } + } + if (extendsProfileID == null) { + printStackTraceAndExit(potentialExtendsProfileID + " that is supposed to extend '" + + profileID + "' is not present in the java.security file or any appended files."); + } + } else { + printStackTraceAndExit(potentialExtendsProfileID + " that is supposed to extend '" + + profileID + "' is not a full profile name."); + } + + // Recursively call init() on extended profile. + init(extendsProfileID); + + // Perform update based on current profile. + update(profileID); + } else { + try { + List allInfo = new ArrayList<>(); + // Load restricted security providers from java.security properties. + initProviders(profileID, allInfo); + // Load restricted security properties from java.security properties. + loadProperties(profileID, allInfo); + + String hashProperty = profileID + ".desc.hash"; + String hashValue = securityProps.getProperty(hashProperty); + if (hashValue != null) { + // Save info to be hashed and expected result to be checked later. + profilesHashes.put(profileID, hashValue); + profilesInfo.put(profileID, allInfo); + } else if (!isFIPS1402Profile(profileID)) { + // A hash is mandatory, but not for older 140-2 profiles. + printStackTraceAndExit(profileID + " is a base profile, so a hash value is mandatory."); + } + } catch (Exception e) { + if (debug != null) { + debug.println("Unable to initialize restricted security mode."); + } + printStackTraceAndExit(e); + } + } + + if (debug != null) { + debug.println("Initialization of restricted security properties for '" + profileID + "' completed."); + } + } + + /** + * Initialize restricted security properties. + */ + private void update(String profileExtensionId) { + try { + List allInfo = new ArrayList<>(); + // Load restricted security providers from java.security properties. + updateProviders(profileExtensionId, allInfo); + // Load restricted security properties from java.security properties. + loadProperties(profileExtensionId, allInfo); + + String hashProperty = profileExtensionId + ".desc.hash"; + String hashValue = securityProps.getProperty(hashProperty); + + // Hash value is optional in extension profiles. + if (hashValue != null) { + // Save info to be hashed and expected result to be checked later. + profilesHashes.put(profileID, hashValue); + profilesInfo.put(profileID, allInfo); + } + } catch (Exception e) { + if (debug != null) { + debug.println("Unable to update restricted security properties for '" + profileExtensionId + "'."); + } + printStackTraceAndExit(e); + } + } + + private void parseProvider(String providerInfo, int providerPos, boolean update) { + if (debug != null) { + debug.println("\t\tLoading provider in position " + providerPos); + } + + checkProviderFormat(providerInfo, update); + + int pos = providerInfo.indexOf('['); + String providerName = (pos < 0) ? providerInfo.trim() : providerInfo.substring(0, pos).trim(); + // Provider with argument (provider name + optional argument). + if (update) { + providers.set(providerPos - 1, providerName); + } else { + providers.add(providerPos - 1, providerName); + } + + // Remove the provider's optional arguments if there are. + pos = providerName.indexOf(' '); + if (pos >= 0) { + providerName = providerName.substring(0, pos); + } + providerName = providerName.trim(); + + // Remove argument, e.g. -NSS-FIPS, if present. + pos = providerName.indexOf('-'); + if (pos >= 0) { + providerName = providerName.substring(0, pos); + } + + // Provider name defined in provider construction method. + providerName = getProvidersSimpleName(providerName); + boolean providerChanged = false; + if (update) { + String previousProviderName = providersSimpleName.get(providerPos - 1); + providerChanged = !previousProviderName.equals(providerName); + providersSimpleName.set(providerPos - 1, providerName); + } else { + providersSimpleName.add(providerPos - 1, providerName); + } + + if (debug != null) { + debug.println("\t\tLoaded provider in position " + providerPos + " named: " + providerName); + } + + // Set the provided constraints for this provider. + setConstraints(providerName, providerInfo, providerChanged); + } + + private void removeProvider(String profileExtensionId, int providerPos) { + if (debug != null) { + debug.println("\t\tRemoving provider in position " + providerPos); + } + + int numOfExistingProviders = providersSimpleName.size(); + + // If this is the last provider, remove from all lists. + if (providerPos == numOfExistingProviders) { + if (debug != null) { + debug.println("\t\t\tLast provider. Only one to be removed."); + } + String providerRemoved = providersSimpleName.remove(providerPos - 1); + providers.remove(providerPos - 1); + providerConstraints.remove(providerRemoved); + + if (debug != null) { + debug.println("\t\tProvider " + providerRemoved + " removed."); + } + return; + } + + // If there's more, check that all of the subsequent ones are set to be removed. + for (int i = numOfExistingProviders; i >= providerPos; i--) { + if (debug != null) { + debug.println("\t\t\tNot the last provider. More to be removed."); + } + + String providerInfo = securityProps.getProperty(profileExtensionId + ".jce.provider." + i); + if ((providerInfo == null) || !providerInfo.isBlank()) { + printStackTraceAndExit( + "Cannot specify an empty provider in position " + + providerPos + " when non-empty ones are specified after it."); + } + + // Remove all of the providers that are set to empty. + String providerRemoved = providersSimpleName.remove(i - 1); + providers.remove(i - 1); + providerConstraints.remove(providerRemoved); + + if (debug != null) { + debug.println("\t\tProvider " + providerRemoved + " removed."); + } + } + } + + /** + * Load restricted security provider. + */ + private void initProviders(String profileID, List allInfo) { + if (debug != null) { + debug.println("\tLoading providers of restricted security profile."); + } + + for (int pNum = 1;; ++pNum) { + String property = profileID + ".jce.provider." + pNum; + String providerInfo = securityProps.getProperty(property); + + if (providerInfo == null) { + break; + } + + if (providerInfo.isBlank()) { + printStackTraceAndExit( + "Cannot specify an empty provider in position " + + pNum + ". Nothing specified before."); + } + + allInfo.add(property + "=" + providerInfo); + + parseProvider(providerInfo, pNum, false); + } + + if (providers.isEmpty()) { + printStackTraceAndExit( + "No providers are specified as part of the Restricted Security profile."); + } + + if (debug != null) { + debug.println("\tProviders of restricted security profile successfully loaded."); + } + } + + private void updateProviders(String profileExtensionId, List allInfo) { + boolean removedProvider = false; + int numOfExistingProviders = providersSimpleName.size(); + // Deal with update of existing providers. + for (int i = 1; i <= numOfExistingProviders; i++) { + String property = profileExtensionId + ".jce.provider." + i; + String providerInfo = securityProps.getProperty(property); + if (providerInfo != null) { + allInfo.add(property + "=" + providerInfo); + if (!providerInfo.isBlank()) { + // Update the specific provider. + parseProvider(providerInfo, i, true); + } else { + // Remove provider(s) after checking. + removeProvider(profileExtensionId, i); + removedProvider = true; + break; + } + } + } + + // Deal with additional providers added. + for (int i = numOfExistingProviders + 1;; i++) { + String property = profileExtensionId + ".jce.provider." + i; + String providerInfo = securityProps.getProperty(property); + + if (providerInfo == null) { + break; + } + + if (providerInfo.isBlank()) { + printStackTraceAndExit( + "Cannot specify an empty provider in position " + + i + ". Nothing specified before."); + } + + if (removedProvider) { + printStackTraceAndExit( + "Cannot add a provider in position " + i + + " after removing the ones in previous positions."); + } + + allInfo.add(property + "=" + providerInfo); + + parseProvider(providerInfo, i, false); + } + } + + /** + * Load restricted security properties. */ - private static String getProvidersSimpleName(String providerName) { - if (providerName.equals("com.sun.security.sasl.Provider")) { - // The main class for the SunSASL provider is com.sun.security.sasl.Provider. - return "SunSASL"; + private void loadProperties(String profileID, List allInfo) { + if (debug != null) { + debug.println("\tLoading properties of restricted security profile."); + } + + setProperty("descName", profileID + ".desc.name", allInfo); + if (setProperty("descIsDefaultString", profileID + ".desc.default", allInfo)) { + descIsDefault = Boolean.parseBoolean(profileProperties.get("descIsDefaultString")); + } + if (setProperty("descIsFIPSString", profileID + ".desc.fips", allInfo)) { + descIsFIPS = Boolean.parseBoolean(profileProperties.get("descIsFIPSString")); + } + setProperty("descNumber", profileID + ".desc.number", allInfo); + setProperty("descPolicy", profileID + ".desc.policy", allInfo); + setProperty("descSunsetDate", profileID + ".desc.sunsetDate", allInfo); + + setProperty("jdkTlsDisabledNamedCurves", + profileID + ".tls.disabledNamedCurves", allInfo); + setProperty("jdkTlsDisabledAlgorithms", + profileID + ".tls.disabledAlgorithms", allInfo); + setProperty("jdkTlsEphemeralDHKeySize", + profileID + ".tls.ephemeralDHKeySize", allInfo); + setProperty("jdkTlsLegacyAlgorithms", + profileID + ".tls.legacyAlgorithms", allInfo); + setProperty("jdkCertpathDisabledAlgorithms", + profileID + ".jce.certpath.disabledAlgorithms", allInfo); + setProperty("jdkSecurityLegacyAlgorithms", + profileID + ".jce.legacyAlgorithms", allInfo); + setProperty("keyStoreType", + profileID + ".keystore.type", allInfo); + setProperty("keyStore", + profileID + ".javax.net.ssl.keyStore", allInfo); + + setProperty("jdkSecureRandomProvider", + profileID + ".securerandom.provider", allInfo); + setProperty("jdkSecureRandomAlgorithm", + profileID + ".securerandom.algorithm", allInfo); + setProperty("jdkFipsMode", + profileID + ".fips.mode", allInfo); + + if (debug != null) { + debug.println("\tProperties of restricted security profile successfully loaded."); + } + } + + private void setConstraints(String providerName, String providerInfo, boolean providerChanged) { + if (debug != null) { + debug.println("\t\tLoading constraints for security provider: " + providerName); + } + + List constraints = new ArrayList<>(); + + providerInfo = providerInfo.replaceAll("\\s+", ""); + + // Check whether constraints are specified for this provider. + Pattern p = Pattern.compile("\\[.+\\]"); + Matcher m = p.matcher(providerInfo); + if (!m.find()) { + if (debug != null) { + debug.println("\t\t\tNo constraints for security provider: " + providerName); + } + providerConstraints.put(providerName, constraints); + return; + } + + // Check whether constraints are properly specified. + final String typeRE = "\\w+"; + final String algoRE = "[A-Za-z0-9./_-]+"; + final String attrRE = "[A-Za-z0-9=*|.:]+"; + final String consRE = "\\{(" + typeRE + "),(" + algoRE + "),(" + attrRE + ")\\}"; + p = Pattern.compile( + "\\[" + + "([+-]?)" // option to append or remove + + consRE // at least one constraint + + "(," + consRE + ")*" // more constraints [optional] + + "\\]"); + m = p.matcher(providerInfo); + + if (!m.find()) { + printStackTraceAndExit("Incorrect constraint definition for provider " + providerName); + } + + String action = m.group(1); + + // Parse all provided constraints. + p = Pattern.compile(consRE); + m = p.matcher(providerInfo); + + while (m.find()) { + String inType = m.group(1); + String inAlgorithm = m.group(2); + String inAttributes = m.group(3); + + // Each attribute must includes 2 fields (key and value) or *. + if (!isAsterisk(inAttributes)) { + String[] attributeArray = inAttributes.split(":"); + for (String attribute : attributeArray) { + String[] in = attribute.split("=", 2); + if (in.length != 2) { + printStackTraceAndExit( + "Constraint attributes format is incorrect: " + providerInfo); + } + } + } + Constraint constraint = new Constraint(inType, inAlgorithm, inAttributes); + constraints.add(constraint); + } + + // Differeriante between add, remove and override. + if (isNullOrBlank(action)) { + providerConstraints.put(providerName, constraints); } else { - // Remove the provider's class package names if present. - int pos = providerName.lastIndexOf('.'); - if (pos >= 0) { - providerName = providerName.substring(pos + 1); + if (providerChanged) { + printStackTraceAndExit( + "Cannot append or remove constraints since the provider " + providerName + + " wasn't in this position in the profile extended."); + } + List existingConstraints = providerConstraints.get(providerName); + if (existingConstraints == null) { + existingConstraints = new ArrayList<>(); + providerConstraints.put(providerName, existingConstraints); + } + if (action.equals("+")) { // Appending constraints. + existingConstraints.addAll(constraints); + } else { // Removing constraints. + for (Constraint toRemove : constraints) { + if (!existingConstraints.remove(toRemove)) { + printStackTraceAndExit( + "Constraint " + toRemove + "is not part of existing constraints."); + } + } + } + } + + if (debug != null) { + debug.println("\t\t\tSuccessfully loaded constraints for security provider: " + providerName); + } + } + + private void checkHashValues() { + for (Map.Entry entry : profilesHashes.entrySet()) { + String profileID = entry.getKey(); + String hashValue = entry.getValue(); + List allInfo = profilesInfo.get(profileID); + + if (debug != null) { + debug.println("Calculating hash for '" + profileID + "'."); + } + String[] hashInfo = hashValue.split(":"); + if (hashInfo.length != 2) { + printStackTraceAndExit("Incorrect definition of hash value for " + profileID); + } + + String digestAlgo = hashInfo[0].trim(); + String expectedHash = hashInfo[1].trim(); + try { + MessageDigest md = MessageDigest.getInstance(digestAlgo); + byte[] allInfoArray = allInfo.stream() + .sorted() + .collect(Collectors.joining("\n")) + .getBytes(StandardCharsets.UTF_8); + byte[] resultHashArray = md.digest(allInfoArray); + StringBuilder hexString = new StringBuilder(); + for (byte hashByte : resultHashArray) { + hexString.append(String.format("%02x", hashByte & 0xff)); + } + String resultHashHex = hexString.toString(); + if (debug != null) { + debug.println("\tCalculated hash for '" + profileID + "': " + resultHashHex); + debug.println("\tExpected hash for '" + profileID + "': " + expectedHash); + } + if (!resultHashHex.equalsIgnoreCase(expectedHash)) { + printStackTraceAndExit("Hex produced from profile is not the same is a " + + "base profile, so a hash value is mandatory."); + } + } catch (NoSuchAlgorithmException nsae) { + if (debug != null) { + debug.println("The hash algorithm specified for '" + + profileID + "' is not available."); + } + printStackTraceAndExit(nsae); } - // Provider without package names. - return providerName; } } @@ -1114,82 +1568,137 @@ private void listAvailableProfiles() { } } - /** - * List the RestrictedSecurity profile currently used. - */ - private void listUsedProfile() { - System.out.println(); - System.out.println("Utilized Restricted Security Profile Info:"); - System.out.println("=========================================="); - System.out.println("The Restricted Security profile used is: " + profileID); - System.out.println(); - printProfile(profileID); - } - private void printProfile(String profileToPrint) { + Set propertyNames = securityProps.stringPropertyNames(); + List descKeys = new ArrayList<>(); + List providers = new ArrayList<>(); + List restrictions = new ArrayList<>(); + for (String propertyName : propertyNames) { + if (propertyName.startsWith(profileToPrint + ".desc.") || propertyName.startsWith(profileToPrint + ".fips.")) { + descKeys.add(propertyName + securityProps.getProperty(propertyName)); + } else if (propertyName.startsWith(profileToPrint + ".jce.provider.")) { + providers.add(propertyName + securityProps.getProperty(propertyName)); + } else if (propertyName.startsWith(profileToPrint)) { + restrictions.add(propertyName + securityProps.getProperty(propertyName)); + } + } + System.out.println(profileToPrint + " Profile Info:"); System.out.println("=========================================="); - printProperty(profileToPrint + ".desc.name: ", - securityProps.getProperty(profileToPrint + ".desc.name")); - printProperty(profileToPrint + ".desc.default: ", - securityProps.getProperty(profileToPrint + ".desc.default")); - printProperty(profileToPrint + ".desc.fips: ", - securityProps.getProperty(profileToPrint + ".desc.fips")); - printProperty(profileToPrint + ".fips.mode: ", - securityProps.getProperty(profileToPrint + ".fips.mode")); - printProperty(profileToPrint + ".desc.number: ", - parseProperty(securityProps.getProperty(profileToPrint + ".desc.number"))); - printProperty(profileToPrint + ".desc.policy: ", - parseProperty(securityProps.getProperty(profileToPrint + ".desc.policy"))); - printProperty(profileToPrint + ".desc.sunsetDate: ", - parseProperty(securityProps.getProperty(profileToPrint + ".desc.sunsetDate"))); + for (String descKey : descKeys) { + System.out.println(descKey); + } System.out.println(); // List providers. System.out.println(profileToPrint + " Profile Providers:"); System.out.println("==============================================="); - for (int pNum = 1;; ++pNum) { - String providerInfo = securityProps - .getProperty(profileToPrint + ".jce.provider." + pNum); - - if ((providerInfo == null) || providerInfo.trim().isEmpty()) { - break; - } - printProperty(profileToPrint + ".jce.provider." + pNum + ": ", providerInfo); + for (String provider : providers) { + System.out.println(provider); } System.out.println(); // List profile restrictions. System.out.println(profileToPrint + " Profile Restrictions:"); System.out.println("=================================================="); - printProperty(profileToPrint + ".tls.disabledNamedCurves: ", - parseProperty(securityProps.getProperty(profileToPrint + ".tls.disabledNamedCurves"))); - printProperty(profileToPrint + ".tls.disabledAlgorithms: ", - parseProperty(securityProps.getProperty(profileToPrint + ".tls.disabledAlgorithms"))); - printProperty(profileToPrint + ".tls.ephemeralDHKeySize: ", - parseProperty(securityProps.getProperty(profileToPrint + ".tls.ephemeralDHKeySize"))); - printProperty(profileToPrint + ".tls.legacyAlgorithms: ", - parseProperty(securityProps.getProperty(profileToPrint + ".tls.legacyAlgorithms"))); - printProperty(profileToPrint + ".jce.certpath.disabledAlgorithms: ", - parseProperty(securityProps.getProperty(profileToPrint + ".jce.certpath.disabledAlgorithms"))); - printProperty(profileToPrint + ".jce.legacyAlgorithms: ", - parseProperty(securityProps.getProperty(profileToPrint + ".jce.legacyAlgorithms"))); + for (String restriction : restrictions) { + System.out.println(restriction); + } System.out.println(); + } - printProperty(profileToPrint + ".keystore.type: ", - parseProperty(securityProps.getProperty(profileToPrint + ".keystore.type"))); - printProperty(profileToPrint + ".javax.net.ssl.keyStore: ", - parseProperty(securityProps.getProperty(profileToPrint + ".javax.net.ssl.keyStore"))); - printProperty(profileToPrint + ".securerandom.provider: ", - parseProperty(securityProps.getProperty(profileToPrint + ".securerandom.provider"))); - printProperty(profileToPrint + ".securerandom.algorithm: ", - parseProperty(securityProps.getProperty(profileToPrint + ".securerandom.algorithm"))); - System.out.println(); + /** + * Only set a property if the value is not null. + * + * @param property the property to be set + * @param propertyKey the property key in the java.security file + * @return whether the property was set + */ + private boolean setProperty(String property, String propertyKey, List allInfo) { + if (debug != null) { + debug.println("Setting property: " + property); + } + String value = securityProps.getProperty(propertyKey); + value = parseProperty(value); + String newValue = null; + if (value != null) { + // Add to info to create hash. + allInfo.add(propertyKey + "=" + value); + + // Check if property overrides, adds to or removes from previous value. + String existingValue = profileProperties.get(property); + if (value.startsWith("+")) { + if (!isPropertyAppendable(property)) { + printStackTraceAndExit("Property '" + property + "' is not appendable."); + } else { + // Append additional values to property. + value = value.substring(1).trim(); + + // Take existing value of property into account, if applicable. + if (existingValue == null) { + printStackTraceAndExit("Property '" + property + "' does not exist in parent profile. Cannot append."); + } else if (existingValue.isBlank()) { + newValue = value; + } else { + newValue = (value.isBlank()) ? existingValue : existingValue + ", " + value; + } + } + } else if (value.startsWith("-")) { + if (!isPropertyAppendable(property)) { + printStackTraceAndExit("Property '" + property + "' is not appendable."); + } else { + // Remove values from property. + value = value.substring(1).trim(); + if (!value.isBlank()) { + List existingValues = Stream.of(existingValue.split(",")) + .map(v -> v.trim()) + .collect(Collectors.toList()); + String[] valuesToRemove = value.split(","); + for (String valueToRemove : valuesToRemove) { + if (!existingValues.remove(valueToRemove.trim())) { + printStackTraceAndExit("Value '" + valueToRemove + "' is not in existing values."); + } + } + newValue = String.join(",", existingValues); + } else { + // Nothing to do. Use existing value of property into account, if available. + if (existingValue == null) { + printStackTraceAndExit("Property '" + property + "' does not exist in parent profile. Cannot remove."); + } else if (existingValue.isBlank()) { + newValue = value; + } else { + newValue = existingValue; + } + } + } + } else { + newValue = value; + } + profileProperties.put(property, newValue); + return true; + } + if (debug != null) { + debug.println("Nothing to set. Value of property " + property + " is null."); + } + + return false; + } + + private String getProperty(String property) { + return profileProperties.get(property); } - private void printProperty(String name, String value) { - String valueToPrint = (value.isEmpty()) ? "NOT AVAILABLE" : value; - System.out.println(name + valueToPrint); + private static boolean isPropertyAppendable(String property) { + switch (property) { + case "jdkCertpathDisabledAlgorithms": + case "jdkSecurityLegacyAlgorithms": + case "jdkTlsDisabledAlgorithms": + case "jdkTlsDisabledNamedCurves": + case "jdkTlsLegacyAlgorithms": + return true; + default: + return false; + } } /** @@ -1206,64 +1715,77 @@ private static String parseProperty(String string) { return string; } - /** - * Check if the brackets are balanced. - * - * @param string input string for checking - * @return true if the brackets are balanced - */ - private static boolean areBracketsBalanced(String string) { - Deque deque = new LinkedList<>(); + private static void checkProviderFormat(String providerInfo, boolean update) { + final String nameRE = "[A-Za-z0-9.-]+"; + final String fileRE = "[A-Za-z0-9./\\\\${}]+"; + Pattern p = Pattern.compile( + "^(" + nameRE + ")" // provider name + + "\\s*" + + "(" + fileRE + ")?" // configuration file [optional] + + "\\s*" + + "(\\[" // constraints [optional] + + "\\s*" + + "([+-])?" // action [optional] + + "[A-Za-z0-9{}.=*|:,/_\\s-]+" // constraint definition + + "\\])?" + + "\\s*" + + "$"); + Matcher m = p.matcher(providerInfo); + if (m.find()) { + String providerName = m.group(1); + if (providerName.indexOf('.') <= 0) { + printStackTraceAndExit("Provider must be specified using" + + " the fully-qualified class name: " + providerName); + } - for (char ch : string.toCharArray()) { - switch (ch) { - case '{': - deque.addFirst('}'); - break; - case '[': - deque.addFirst(']'); - break; - case '(': - deque.addFirst(')'); - break; - case '}': - case ']': - case ')': - if (deque.isEmpty() || (deque.removeFirst().charValue() != ch)) { - return false; - } - break; - default: - break; + String action = m.group(4); + if (!update && !isNullOrBlank(action)) { + printStackTraceAndExit("You cannot add or remove to provider " + + m.group(1) + ". This is the base profile."); } + } else { + printStackTraceAndExit("Provider format is incorrect: " + providerInfo); } - return deque.isEmpty(); } + } - /** - * Check if the input string is asterisk (*). - * - * @param string input string for checking - * @return true if the input string is asterisk - */ - private static boolean isAsterisk(String string) { - return "*".equals(string); + /** + * A class representing the constraints of a provider. + */ + private static final class Constraint { + final String type; + final String algorithm; + final String attributes; + + Constraint(String type, String algorithm, String attributes) { + super(); + this.type = type; + this.algorithm = algorithm; + this.attributes = attributes; } - /** - * A class representing the constraints of a provider. - */ - private static final class Constraint { - final String type; - final String algorithm; - final String attributes; + @Override + public String toString() { + return "{" + type + ", " + algorithm + ", " + attributes + "}"; + } - Constraint(String type, String algorithm, String attributes) { - super(); - this.type = type; - this.algorithm = algorithm; - this.attributes = attributes; + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; } + if (obj instanceof Constraint) { + Constraint other = (Constraint) obj; + return Objects.equals(type, other.type) + && Objects.equals(algorithm, other.algorithm) + && Objects.equals(attributes, other.attributes); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(type, algorithm, attributes); } } } diff --git a/src/java.base/share/classes/sun/security/jca/Providers.java b/src/java.base/share/classes/sun/security/jca/Providers.java index 7256308c12..2cb3ced6da 100644 --- a/src/java.base/share/classes/sun/security/jca/Providers.java +++ b/src/java.base/share/classes/sun/security/jca/Providers.java @@ -23,10 +23,18 @@ * questions. */ +/* + * =========================================================================== + * (c) Copyright IBM Corp. 2024, 2024 All Rights Reserved + * =========================================================================== + */ + package sun.security.jca; import java.security.Provider; +import openj9.internal.security.RestrictedSecurity; + /** * Collection of methods to get and set provider list. Also includes * special code for the provider list during JAR verification. @@ -52,6 +60,7 @@ public class Providers { // triggers a getInstance() call (although that should not happen) providerList = ProviderList.EMPTY; providerList = ProviderList.fromSecurityProperties(); + RestrictedSecurity.checkHashValues(); } private Providers() { diff --git a/src/java.base/share/conf/security/java.security b/src/java.base/share/conf/security/java.security index 11b3de0ac1..b0aceeb0d9 100644 --- a/src/java.base/share/conf/security/java.security +++ b/src/java.base/share/conf/security/java.security @@ -163,6 +163,7 @@ RestrictedSecurity.NSS.140-2.securerandom.algorithm = PKCS11 RestrictedSecurity.OpenJCEPlusFIPS.FIPS140-3.desc.name = OpenJCEPlusFIPS Cryptographic Module FIPS 140-3 RestrictedSecurity.OpenJCEPlusFIPS.FIPS140-3.desc.default = true RestrictedSecurity.OpenJCEPlusFIPS.FIPS140-3.desc.fips = true +RestrictedSecurity.OpenJCEPlusFIPS.FIPS140-3.desc.hash = SHA256:f5b04d07f6fd5d11374b23dd96110b231d0241096563ae55cf3ea6fb1788d97c RestrictedSecurity.OpenJCEPlusFIPS.FIPS140-3.desc.number = Certificate #XXX RestrictedSecurity.OpenJCEPlusFIPS.FIPS140-3.desc.policy = https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/ RestrictedSecurity.OpenJCEPlusFIPS.FIPS140-3.desc.sunsetDate = 2026-09-21