From 2e12a123c9955b7360fd752a6a33d59b6271c5af Mon Sep 17 00:00:00 2001 From: Sean Coffey Date: Fri, 14 Jul 2023 07:31:09 +0000 Subject: [PATCH] 8281658: Add a security category to the java -XshowSettings option Reviewed-by: rriggs, mullan --- .../classes/sun/launcher/LauncherHelper.java | 12 +- .../sun/launcher/SecuritySettings.java | 217 ++++++++++++++++++ .../launcher/resources/launcher.properties | 10 + .../security/Security/ConfigFileTest.java | 18 +- test/jdk/tools/launcher/Settings.java | 67 +++++- 5 files changed, 318 insertions(+), 6 deletions(-) create mode 100644 src/java.base/share/classes/sun/launcher/SecuritySettings.java diff --git a/src/java.base/share/classes/sun/launcher/LauncherHelper.java b/src/java.base/share/classes/sun/launcher/LauncherHelper.java index abd0acbf75c..8985077df37 100644 --- a/src/java.base/share/classes/sun/launcher/LauncherHelper.java +++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java @@ -47,8 +47,8 @@ import java.lang.module.ModuleDescriptor.Requires; import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; import java.lang.module.ResolvedModule; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.math.BigDecimal; @@ -78,7 +78,6 @@ import java.util.jar.Manifest; import java.util.stream.Collectors; import java.util.stream.Stream; -import jdk.internal.util.OperatingSystem; import jdk.internal.misc.MainMethodFinder; import jdk.internal.misc.PreviewFeatures; import jdk.internal.misc.VM; @@ -86,6 +85,7 @@ import jdk.internal.module.ModuleBootstrap; import jdk.internal.module.Modules; import jdk.internal.platform.Container; import jdk.internal.platform.Metrics; +import jdk.internal.util.OperatingSystem; import sun.util.calendar.ZoneInfoFile; /** @@ -172,6 +172,10 @@ public final class LauncherHelper { case "locale": printLocale(); break; + case "security": + var opt = opts.length > 2 ? opts[2].trim() : "all"; + SecuritySettings.printSecuritySettings(opt, ostream); + break; case "system": if (OperatingSystem.isLinux()) { printSystemMetrics(); @@ -181,6 +185,7 @@ public final class LauncherHelper { printVmSettings(initialHeapSize, maxHeapSize, stackSize); printProperties(); printLocale(); + SecuritySettings.printSecuritySummarySettings(ostream); if (OperatingSystem.isLinux()) { printSystemMetrics(); } @@ -318,9 +323,10 @@ public final class LauncherHelper { ostream.print(INDENT + INDENT); } } + ostream.println(); } - public static void printSystemMetrics() { + private static void printSystemMetrics() { Metrics c = Container.metrics(); ostream.println("Operating System Metrics:"); diff --git a/src/java.base/share/classes/sun/launcher/SecuritySettings.java b/src/java.base/share/classes/sun/launcher/SecuritySettings.java new file mode 100644 index 00000000000..c2bc67761c2 --- /dev/null +++ b/src/java.base/share/classes/sun/launcher/SecuritySettings.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.launcher; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import java.io.IOException; +import java.io.PrintStream; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Security; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.stream.Collectors; + +import jdk.internal.access.SharedSecrets; + +/** + * A utility class for security libs functionality + * in the -XshowSettings:security output + */ +public final class SecuritySettings { + + private static final String INDENT = " "; + private static final String TWOINDENT = INDENT + INDENT; + private static final String THREEINDENT = TWOINDENT + INDENT; + private static final String PROV_INFO_STRING = "Provider information: "; + private static PrintStream ostream = null; + + static void printSecuritySettings(String arg, PrintStream stream) { + ostream = stream; + switch (arg) { + case "properties" -> printSecurityProperties(); + case "providers" -> printSecurityProviderConfig(true); + case "tls" -> printSecurityTLSConfig(true); + case "all" -> printAllSecurityConfig(); + default -> ostream.println( + "\nUnrecognized security subcommand. Valid values are " + + "\"all\", \"properties\", \"providers\", \"tls\". See \"java -X\"\n"); + } + } + + // A non-verbose description of some core security configuration settings + static void printSecuritySummarySettings(PrintStream stream) { + ostream = stream; + ostream.println("Security settings summary: " + "\n" + + INDENT + "See \"java -X\" for verbose security settings options"); + printSecurityProviderConfig(false); + printSecurityTLSConfig(false); + } + + static void printAllSecurityConfig() { + ostream.println("Security settings:"); + printSecurityProperties(); + printSecurityProviderConfig(true); + printSecurityTLSConfig(true); + } + + private static void printSecurityProperties() { + ostream.println(INDENT + "Security properties:"); + Properties p = SharedSecrets.getJavaSecurityPropertiesAccess().getInitialProperties(); + for (String key : p.stringPropertyNames().stream().sorted().toList()) { + String val = p.getProperty(key); + if (val.length() > 60) { + splitLongPropertyLines(key, val); + } else { + ostream.println(TWOINDENT + key + "=" + val); + } + } + ostream.println(); + } + + private static void splitLongPropertyLines(String key, String val) { + // split long property values which use well known separator + if (val.contains(",") || val.contains(";")) { + String separator = (val.contains(",")) ? "," : ";"; + ostream.println(TWOINDENT + key + "="); + String[] values = val.split(separator); + String lastValue = values[values.length -1].trim(); + List.of(values).forEach( + s -> ostream.println(THREEINDENT + s.trim() + + (s.trim().equals(lastValue) ? "" : separator))); + } else { + ostream.println(TWOINDENT + key + "=" + val); + } + } + + private static void printSecurityTLSConfig(boolean verbose) { + SSLSocket ssls; + SSLContext sslContext; + try { + sslContext = SSLContext.getDefault(); + ssls = (SSLSocket)sslContext.getSocketFactory().createSocket(); + } catch (IOException | NoSuchAlgorithmException e) { + ostream.println(INDENT + "Failed to create SSL socket"); + ostream.println(INDENT + e + "\n"); + return; + } + + ostream.println(INDENT + "Security TLS configuration (" + + sslContext.getProvider().getName() + " provider):"); + ostream.println(TWOINDENT + "Enabled Protocols:"); + for (String s : ssls.getEnabledProtocols()) { + ostream.println(THREEINDENT + s); + } + + if (verbose) { + ostream.println("\n" + TWOINDENT + "Enabled Cipher Suites:"); + for (String s : ssls.getEnabledCipherSuites()) { + ostream.println(THREEINDENT + s); + } + } + ostream.println(); + } + + private static void printSecurityProviderConfig(boolean verbose) { + ostream.println(INDENT + "Security provider static configuration: (in order of preference)"); + for (Provider p : Security.getProviders()) { + if (verbose) { + // separate the views out + ostream.println(TWOINDENT + "-".repeat(40)); + } + ostream.println(TWOINDENT + "Provider name: " + p.getName()); + if (verbose) { + ostream.println(wrappedString(PROV_INFO_STRING + p.getInfo(), 80, + TWOINDENT, THREEINDENT)); + ostream.println(TWOINDENT + "Provider services: (type : algorithm)"); + Set services = p.getServices(); + Set keys = Collections.list(p.keys()) + .stream() + .map(String.class::cast) + .filter(s -> s.startsWith("Alg.Alias.")) + .collect(Collectors.toSet()); + if (!services.isEmpty()) { + services.stream() + .sorted(Comparator.comparing(Provider.Service::getType) + .thenComparing(Provider.Service::getAlgorithm)) + .forEach(ps -> { + ostream.println(THREEINDENT + + ps.getType() + "." + ps.getAlgorithm()); + List aliases = keys + .stream() + .filter(s -> s.startsWith("Alg.Alias." + ps.getType())) + .filter(s -> p.getProperty(s).equals(ps.getAlgorithm())) + .map(s -> s.substring(("Alg.Alias." + ps.getType() + ".").length())) + .toList(); + + if (!aliases.isEmpty()) { + ostream.println(wrappedString( + aliases.stream() + .collect(Collectors.joining(", ", INDENT + " aliases: [", "]")), + 80, " " + TWOINDENT, INDENT + THREEINDENT)); + } + }); + } else { + ostream.println(THREEINDENT + ""); + } + } + } + if (verbose) { + ostream.println(); + } + } + + // return a string split across multiple lines which aims to limit max length + private static String wrappedString(String orig, int limit, + String initIndent, String successiveIndent) { + if (orig == null || orig.isEmpty() || limit <= 0) { + // bad input + return orig; + } + StringBuilder sb = new StringBuilder(); + int widthCount = 0; + for (String s : orig.split(" ")) { + if (widthCount == 0) { + // first iteration only + sb.append(initIndent + s); + widthCount = s.length() + initIndent.length(); + } else { + if (widthCount + s.length() > limit) { + sb.append("\n" + successiveIndent + s); + widthCount = s.length() + successiveIndent.length(); + } else { + sb.append(" " + s); + widthCount += s.length() + 1; + } + } + } + return sb.toString(); + } +} diff --git a/src/java.base/share/classes/sun/launcher/resources/launcher.properties b/src/java.base/share/classes/sun/launcher/resources/launcher.properties index 9960b026d11..7d10527b2a8 100644 --- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties +++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties @@ -170,6 +170,16 @@ java.launcher.X.usage=\n\ \ show all property settings and continue\n\ \ -XshowSettings:vm\n\ \ show all vm related settings and continue\n\ +\ -XshowSettings:security\n\ +\ show all security settings and continue\n\ +\ -XshowSettings:security:all\n\ +\ show all security settings and continue\n\ +\ -XshowSettings:security:properties\n\ +\ show security properties and continue\n\ +\ -XshowSettings:security:providers\n\ +\ show static security provider settings and continue\n\ +\ -XshowSettings:security:tls\n\ +\ show TLS related security settings and continue\n\ \ -XshowSettings:system\n\ \ (Linux Only) show host system or container\n\ \ configuration and continue\n\ diff --git a/test/jdk/java/security/Security/ConfigFileTest.java b/test/jdk/java/security/Security/ConfigFileTest.java index e6f54a7491e..fe022fc45ce 100644 --- a/test/jdk/java/security/Security/ConfigFileTest.java +++ b/test/jdk/java/security/Security/ConfigFileTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ import java.util.Optional; /* * @test * @summary Throw error if default java.security file is missing - * @bug 8155246 8292297 8292177 + * @bug 8155246 8292297 8292177 8281658 * @library /test/lib * @run main ConfigFileTest */ @@ -71,6 +71,10 @@ public class ConfigFileTest { copyJDK(jdkTestDir, copyJdkDir); String extraPropsFile = Path.of(System.getProperty("test.src"), "override.props").toString(); + // sanity test -XshowSettings:security option + exerciseShowSettingsSecurity(copiedJava.toString(), "-cp", System.getProperty("test.classes"), + "-Djava.security.debug=all", "-XshowSettings:security", "ConfigFileTest", "runner"); + // exercise some debug flags while we're here // regular JDK install - should expect success exerciseSecurity(0, "java", @@ -136,6 +140,16 @@ public class ConfigFileTest { } } + // exercise the -XshowSettings:security launcher + private static void exerciseShowSettingsSecurity(String... args) throws Exception { + ProcessBuilder process = new ProcessBuilder(args); + OutputAnalyzer oa = ProcessTools.executeProcess(process); + oa.shouldHaveExitValue(0) + .shouldContain("Security properties:") + .shouldContain("Security provider static configuration:") + .shouldContain("Security TLS configuration"); + } + private static void copyJDK(Path src, Path dst) throws Exception { Files.walk(src) .skip(1) diff --git a/test/jdk/tools/launcher/Settings.java b/test/jdk/tools/launcher/Settings.java index 76e62ffbe57..29af50d5c02 100644 --- a/test/jdk/tools/launcher/Settings.java +++ b/test/jdk/tools/launcher/Settings.java @@ -25,7 +25,7 @@ import java.io.IOException; /* * @test - * @bug 6994753 7123582 8305950 + * @bug 6994753 7123582 8305950 8281658 * @summary tests -XshowSettings options * @modules jdk.compiler * jdk.zipfs @@ -67,6 +67,13 @@ public class Settings extends TestHelper { private static final String VM_SETTINGS = "VM settings:"; private static final String PROP_SETTINGS = "Property settings:"; private static final String LOCALE_SETTINGS = "Locale settings:"; + private static final String SEC_PROPS_SETTINGS = "Security properties:"; + private static final String SEC_SUMMARY_PROPS_SETTINGS = + "Security settings summary:"; + private static final String SEC_PROVIDER_SETTINGS = + "Security provider static configuration:"; + private static final String SEC_TLS_SETTINGS = "Security TLS configuration"; + private static final String BAD_SEC_OPTION_MSG = "Unrecognized security subcommand"; private static final String SYSTEM_SETTINGS = "Operating System Metrics:"; private static final String STACKSIZE_SETTINGS = "Stack Size:"; private static final String TZDATA_SETTINGS = "tzdata version"; @@ -75,6 +82,11 @@ public class Settings extends TestHelper { checkContains(tr, VM_SETTINGS); checkContains(tr, PROP_SETTINGS); checkContains(tr, LOCALE_SETTINGS); + // no verbose security settings unless "security" used + checkNotContains(tr, SEC_PROPS_SETTINGS); + checkContains(tr, SEC_SUMMARY_PROPS_SETTINGS); + checkContains(tr, SEC_PROVIDER_SETTINGS); + checkContains(tr, SEC_TLS_SETTINGS); checkContains(tr, TZDATA_SETTINGS); if (System.getProperty("os.name").contains("Linux")) { checkContains(tr, SYSTEM_SETTINGS); @@ -144,6 +156,54 @@ public class Settings extends TestHelper { checkContains(tr, TZDATA_SETTINGS); } + static void runTestOptionSecurity() throws IOException { + TestResult tr = doExec(javaCmd, "-XshowSettings:security"); + checkNotContains(tr, VM_SETTINGS); + checkNotContains(tr, PROP_SETTINGS); + checkContains(tr, SEC_PROPS_SETTINGS); + checkContains(tr, SEC_PROVIDER_SETTINGS); + checkContains(tr, SEC_TLS_SETTINGS); + } + + static void runTestOptionSecurityProps() throws IOException { + TestResult tr = doExec(javaCmd, "-XshowSettings:security:properties"); + checkContains(tr, SEC_PROPS_SETTINGS); + checkNotContains(tr, SEC_PROVIDER_SETTINGS); + checkNotContains(tr, SEC_TLS_SETTINGS); + // test a well known property for sanity + checkContains(tr, "keystore.type=pkcs12"); + } + + static void runTestOptionSecurityProv() throws IOException { + TestResult tr = doExec(javaCmd, "-XshowSettings:security:providers"); + checkNotContains(tr, SEC_PROPS_SETTINGS); + checkContains(tr, SEC_PROVIDER_SETTINGS); + checkNotContains(tr, SEC_TLS_SETTINGS); + // test a well known Provider for sanity + checkContains(tr, "Provider name: SUN"); + // test for a well known alias (SunJCE: AlgorithmParameterGenerator.DiffieHellman) + checkContains(tr, "aliases: [1.2.840.113549.1.3.1, " + + "DH, OID.1.2.840.113549.1.3.1]"); + } + + static void runTestOptionSecurityTLS() throws IOException { + TestResult tr = doExec(javaCmd, "-XshowSettings:security:tls"); + checkNotContains(tr, SEC_PROPS_SETTINGS); + checkNotContains(tr, SEC_PROVIDER_SETTINGS); + checkContains(tr, SEC_TLS_SETTINGS); + // test a well known TLS config for sanity + checkContains(tr, "TLSv1.2"); + } + + // ensure error message is printed when unrecognized option used + static void runTestOptionBadSecurityOption() throws IOException { + TestResult tr = doExec(javaCmd, "-XshowSettings:security:bad"); + checkContains(tr, BAD_SEC_OPTION_MSG); + // we print all security settings in such scenario + checkNotContains(tr, SEC_PROPS_SETTINGS); + checkNotContains(tr, SEC_PROVIDER_SETTINGS); + checkNotContains(tr, SEC_TLS_SETTINGS); + } static void runTestOptionSystem() throws IOException { TestResult tr = doExec(javaCmd, "-XshowSettings:system"); if (System.getProperty("os.name").contains("Linux")) { @@ -181,6 +241,11 @@ public class Settings extends TestHelper { runTestOptionVM(); runTestOptionProperty(); runTestOptionLocale(); + runTestOptionSecurity(); + runTestOptionSecurityProps(); + runTestOptionSecurityProv(); + runTestOptionSecurityTLS(); + runTestOptionBadSecurityOption(); runTestOptionSystem(); runTestBadOptions(); runTest7123582();