Skip to content

Commit 7859198

Browse files
committed
Implement sunset policy handling with supresssunsetwarning and ignoresunsetexpiration options
Added logic to handle restricted security profile sunset behavior using -Dsemeru.restrictedsecurity.supresssunsetwarning and -Dsemeru.restrictedsecurity.ignoresunsetexpiration system properties. When supresssunsetwarning=true, all sunset warning messages are suppressed; if the profile is expired and ignoresunsetexpiration=false, the JVM exits silently with status 1. When supresssunsetwarning=false. If the profile has expired and ignoresunsetexpiration=false, a fatal error is printed and the JVM terminates. If expired and ignoresunsetexpiration=true, a warning message is printed indicating uncertified cryptography may be active. If the profile will expire within six months, a generic warning message is printed. Signed-off-by: Tao Liu <[email protected]>
1 parent f4ae4de commit 7859198

File tree

4 files changed

+286
-19
lines changed

4 files changed

+286
-19
lines changed

closed/src/java.base/share/classes/openj9/internal/security/RestrictedSecurity.java

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.security.Provider.Service;
3131
import java.time.Clock;
3232
import java.time.LocalDate;
33+
import java.time.Period;
3334
import java.time.format.DateTimeFormatter;
3435
import java.time.format.DateTimeParseException;
3536
import java.util.ArrayList;
@@ -48,6 +49,7 @@
4849
import java.util.stream.Collectors;
4950
import java.util.stream.Stream;
5051

52+
import compiler.lib.ir_framework.driver.irmatching.irrule.constraint.Constraint;
5153
import sun.security.util.Debug;
5254

5355
/**
@@ -63,6 +65,8 @@ public final class RestrictedSecurity {
6365
private static boolean isFIPSEnabled;
6466

6567
private static final boolean allowSetProperties;
68+
private static final boolean suppressSunsetWarning;
69+
private static final boolean ignoreSunsetExpiration;
6670

6771
private static final boolean isNSSSupported;
6872
private static final boolean isOpenJCEPlusCertifiedPlatform;
@@ -104,6 +108,8 @@ public final class RestrictedSecurity {
104108

105109
userEnabledFIPS = Boolean.getBoolean("semeru.fips");
106110
allowSetProperties = Boolean.getBoolean("semeru.fips.allowsetproperties");
111+
suppressSunsetWarning = Boolean.getBoolean("semeru.restrictedsecurity.suppresssunsetwarning");
112+
ignoreSunsetExpiration = Boolean.getBoolean("semeru.restrictedsecurity.ignoresunsetexpiration");
107113

108114
if (userEnabledFIPS) {
109115
if (isFIPSSupported) {
@@ -612,9 +618,37 @@ private static void restrictsCheck() {
612618
printStackTraceAndExit("Restricted security property is null.");
613619
}
614620

615-
// Check if the SunsetDate expired.
616-
if (isPolicySunset(restricts.descSunsetDate)) {
617-
printStackTraceAndExit("Restricted security policy expired.");
621+
int expireMonths = monthsToPolicySunset(restricts.descSunsetDate);
622+
623+
if (suppressSunsetWarning) {
624+
if (expireMonths == 0 && !ignoreSunsetExpiration) {
625+
System.exit(1);
626+
}
627+
} else {
628+
if (expireMonths == 0) { // Check if the SunsetDate expired.
629+
if (ignoreSunsetExpiration) {
630+
System.err.println("The requested restricted security profile " + restricts.profileID
631+
+ " expired on " + restricts.descSunsetDate
632+
+ ": java will stop because certified cryptography use cannot be guaranteed."
633+
+ " Use -Dsemeru.restrictedsecurity.suppresssunsetwarning to stop displaying this message."
634+
+ " The -Dsemeru.restrictedsecurity.ignoresunsetexpiration option has been specified."
635+
+ " WARNING: java will start with the requested restricted security profile but uncertified"
636+
+ " cryptography may be active.");
637+
} else {
638+
printStackTraceAndExit(
639+
"The requested restricted security profile " + restricts.profileID
640+
+ " expired on " + restricts.descSunsetDate
641+
+ ": java will stop because certified cryptography use cannot be guaranteed."
642+
+ " Use -Dsemeru.restrictedsecurity.suppresssunsetwarning to stop displaying this message."
643+
+ " Use the -Dsemeru.restrictedsecurity.ignoresunsetexpiration to allow java to start while"
644+
+ " possibly using uncertified cryptography.");
645+
}
646+
} else if (expireMonths <= 6) { // Check if the SunsetDate will expire within 6 months.
647+
System.err.println("The restricted security profile " + restricts.profileID
648+
+ " is about to expire on " + restricts.descSunsetDate
649+
+ ". after which java will fail to start if this profile is specified."
650+
+ " The latest Semeru Runtimes release may include an updated security profile.");
651+
}
618652
}
619653

620654
// Check secure random settings.
@@ -633,25 +667,30 @@ private static void restrictsCheck() {
633667
* Check if restricted security policy is sunset.
634668
*
635669
* @param descSunsetDate the sunset date from java.security
636-
* @return true if restricted security policy sunset
670+
* @return the months until restricted security policy sunset
637671
*/
638-
private static boolean isPolicySunset(String descSunsetDate) {
639-
boolean isSunset = false;
640-
// Only check if a sunset date is specified in the profile.
641-
if (!isNullOrBlank(descSunsetDate)) {
642-
try {
643-
isSunset = LocalDate.parse(descSunsetDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
644-
.isBefore(LocalDate.now(Clock.systemUTC()));
645-
} catch (DateTimeParseException except) {
646-
printStackTraceAndExit(
647-
"Restricted security policy sunset date is incorrect, the correct format is yyyy-MM-dd.");
648-
}
672+
private static int monthsToPolicySunset(String descSunsetDate) {
673+
// If no sunset date is specified, it will not sunset.
674+
if (isNullOrBlank(descSunsetDate)) {
675+
return Integer.MAX_VALUE;
649676
}
650677

651-
if (debug != null) {
652-
debug.println("Restricted security policy is sunset: " + isSunset);
678+
try {
679+
LocalDate sunsetDate = LocalDate.parse(descSunsetDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
680+
LocalDate now = LocalDate.now(Clock.systemUTC());
681+
682+
if (sunsetDate.isBefore(now)) {
683+
return 0; // already sunset.
684+
}
685+
686+
Period period = Period.between(now, sunsetDate);
687+
return period.getYears() * 12 + period.getMonths();
688+
689+
} catch (DateTimeParseException except) {
690+
printStackTraceAndExit(
691+
"Restricted security policy sunset date is incorrect, the correct format is yyyy-MM-dd.");
653692
}
654-
return isSunset;
693+
return Integer.MAX_VALUE;
655694
}
656695

657696
/**
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* ===========================================================================
3+
* (c) Copyright IBM Corp. 2025, 2025 All Rights Reserved
4+
* ===========================================================================
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation.
9+
*
10+
* IBM designates this particular file as subject to the "Classpath" exception
11+
* as provided by IBM in the LICENSE file that accompanied this code.
12+
*
13+
* This code is distributed in the hope that it will be useful, but WITHOUT
14+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16+
* version 2 for more details (a copy is included in the LICENSE file that
17+
* accompanied this code).
18+
*
19+
* You should have received a copy of the GNU General Public License version
20+
* 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
21+
*
22+
* ===========================================================================
23+
*/
24+
25+
/*
26+
* @test
27+
* @summary Test Restricted Security Mode Policy Sunset
28+
* @library /test/lib
29+
* @run junit TestPolicySunset
30+
*/
31+
32+
import org.junit.jupiter.params.ParameterizedTest;
33+
import org.junit.jupiter.params.provider.Arguments;
34+
import org.junit.jupiter.params.provider.MethodSource;
35+
36+
import java.io.IOException;
37+
import java.nio.charset.StandardCharsets;
38+
import java.nio.file.Files;
39+
import java.nio.file.Path;
40+
import java.nio.file.Paths;
41+
import java.security.Provider;
42+
import java.security.Security;
43+
import java.time.LocalDate;
44+
import java.time.ZoneOffset;
45+
import java.time.format.DateTimeFormatter;
46+
import java.util.stream.Stream;
47+
48+
import jdk.test.lib.process.OutputAnalyzer;
49+
import jdk.test.lib.process.ProcessTools;
50+
51+
public class TestPolicySunset {
52+
53+
private static Path updateExpireSoonSunsetFile(String baseFile) {
54+
try {
55+
LocalDate soonDate = LocalDate.now(ZoneOffset.UTC).plusMonths(1);
56+
String newDate = soonDate.format(DateTimeFormatter.ISO_DATE);
57+
58+
String content = Files.readString(Paths.get(baseFile), StandardCharsets.UTF_8);
59+
String pattern = "(?m)^(RestrictedSecurity\\.Test-Profile-PolicySunset-ExpireSoon\\.desc\\.sunsetDate)\\s*=.*$";
60+
String updated = content.replaceAll(pattern, "$1 = " + newDate);
61+
62+
Path tmp = Files.createTempFile("sunset-java.security.expireSoon.", ".tmp");
63+
Files.writeString(tmp, updated, StandardCharsets.UTF_8);
64+
return tmp;
65+
} catch (IOException e) {
66+
throw new RuntimeException("Failed to update sunset date for ExpireSoon profile", e);
67+
}
68+
}
69+
70+
private static Stream<Arguments> patternMatches_expectedExitValue0() {
71+
String propertyFile = System.getProperty("test.src") + "/sunset-java.security";
72+
String updatedPropertyFile = updateExpireSoonSunsetFile(propertyFile).toString();
73+
74+
return Stream.of(
75+
// 1 - expired; supress=false; ignore=true
76+
Arguments.of("Test-Profile-PolicySunset-Expired",
77+
propertyFile,
78+
"false", "true",
79+
"WARNING: java will start with the requested restricted security profile but uncertified cryptography may be active"),
80+
// 2 - expired; supress=true; ignore=true, no warning
81+
Arguments.of("Test-Profile-PolicySunset-Expired",
82+
propertyFile,
83+
"true", "true",
84+
""),
85+
// 3 - expire soon (<=6 months); supress=false
86+
Arguments.of("Test-Profile-PolicySunset-ExpireSoon",
87+
updatedPropertyFile,
88+
"false", "false",
89+
"The restricted security profile RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon is about to expire"),
90+
// 4 - expire soon (<=6 months); supress=true, no warning
91+
Arguments.of("Test-Profile-PolicySunset-ExpireSoon",
92+
updatedPropertyFile,
93+
"true", "false",
94+
""),
95+
// 5 - not expire (>6 months); no warning
96+
Arguments.of("Test-Profile-PolicySunset-NotExpire",
97+
propertyFile,
98+
"false", "false",
99+
""));
100+
}
101+
102+
private static Stream<Arguments> patternMatches_expectedExitValue1() {
103+
String propertyFile = System.getProperty("test.src") + "/sunset-java.security";
104+
105+
return Stream.of(
106+
// 1 - expired; supress=false; ignore=false
107+
Arguments.of("Test-Profile-PolicySunset-Expired",
108+
propertyFile,
109+
"false", "false",
110+
"Use the -Dsemeru.restrictedsecurity.ignoresunsetexpiration to allow java to start while possibly using uncertified cryptography"),
111+
// 2 - expired; supress=true; ignore=false, no warning
112+
Arguments.of("Test-Profile-PolicySunset-Expired",
113+
propertyFile,
114+
"true", "false",
115+
""));
116+
}
117+
118+
@ParameterizedTest
119+
@MethodSource("patternMatches_expectedExitValue0")
120+
public void shouldContain_expectedExitValue0(String customprofile, String securityPropertyFile, String suppresssunsetwarning, String ignoresunsetexpiration, String expected) throws Exception {
121+
OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(
122+
"-Dsemeru.fips=true",
123+
"-Dsemeru.customprofile=" + customprofile,
124+
"-Djava.security.properties=" + securityPropertyFile,
125+
"-Dsemeru.restrictedsecurity.suppresssunsetwarning=" + suppresssunsetwarning,
126+
"-Dsemeru.restrictedsecurity.ignoresunsetexpiration=" + ignoresunsetexpiration,
127+
"TestPolicySunset"
128+
);
129+
outputAnalyzer.reportDiagnosticSummary();
130+
outputAnalyzer.shouldHaveExitValue(0).shouldMatch(expected);
131+
}
132+
133+
@ParameterizedTest
134+
@MethodSource("patternMatches_expectedExitValue1")
135+
public void shouldContain_expectedExitValue1(String customprofile, String securityPropertyFile, String suppresssunsetwarning, String ignoresunsetexpiration, String expected) throws Exception {
136+
OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(
137+
"-Dsemeru.fips=true",
138+
"-Dsemeru.customprofile=" + customprofile,
139+
"-Djava.security.properties=" + securityPropertyFile,
140+
"-Dsemeru.restrictedsecurity.suppresssunsetwarning=" + suppresssunsetwarning,
141+
"-Dsemeru.restrictedsecurity.ignoresunsetexpiration=" + ignoresunsetexpiration,
142+
"TestPolicySunset"
143+
);
144+
outputAnalyzer.reportDiagnosticSummary();
145+
outputAnalyzer.shouldHaveExitValue(1).shouldMatch(expected);
146+
}
147+
148+
public static void main(String[] args) {
149+
// Something to trigger "properties" debug output.
150+
try {
151+
for (Provider provider : Security.getProviders()) {
152+
System.out.println("Provider Name: " + provider.getName());
153+
System.out.println("Provider Version: " + provider.getVersionStr());
154+
}
155+
} catch (Exception e) {
156+
System.out.println(e);
157+
}
158+
}
159+
}

closed/test/jdk/openj9/internal/security/TestProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ private static Stream<Arguments> patternMatches_expectedExitValue1() {
119119
// 15 - Test property - policy sunset.
120120
Arguments.of("Test-Profile-PolicySunset.Base",
121121
System.getProperty("test.src") + "/property-java.security",
122-
"Restricted security policy expired"),
122+
"Use the -Dsemeru.restrictedsecurity.ignoresunsetexpiration to allow java to start while possibly using uncertified cryptography"),
123123
// 16 - Test property - policy sunset format.
124124
Arguments.of("Test-Profile-PolicySunsetFormat.Base",
125125
System.getProperty("test.src") + "/property-java.security",
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# ===========================================================================
2+
# (c) Copyright IBM Corp. 2025, 2025 All Rights Reserved
3+
# ===========================================================================
4+
# This code is free software; you can redistribute it and/or modify it
5+
# under the terms of the GNU General Public License version 2 only, as
6+
# published by the Free Software Foundation.
7+
#
8+
# IBM designates this particular file as subject to the "Classpath" exception
9+
# as provided by IBM in the LICENSE file that accompanied this code.
10+
#
11+
# This code is distributed in the hope that it will be useful, but WITHOUT
12+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
# version 2 for more details (a copy is included in the LICENSE file that
15+
# accompanied this code).
16+
#
17+
# You should have received a copy of the GNU General Public License version
18+
# 2 along with this work; if not, see <http://www.gnu.org/licenses/>.
19+
# ===========================================================================
20+
21+
#
22+
# Test-Profile-PolicySunset-Expired
23+
#
24+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.desc.name = Test-Profile-PolicySunset-Expired
25+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.desc.default = true
26+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.desc.fips = true
27+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.desc.hash = SHA256:365fb3fe2ff69961980d10d16bd76f6efbf07a723b7a8c58a5f6cd234108eaee
28+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.desc.number = Certificate #XXX
29+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.desc.policy = https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/
30+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.desc.sunsetDate = 2023-09-21
31+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.fips.mode = 140-3
32+
33+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.jce.provider.1 = sun.security.provider.Sun
34+
35+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.securerandom.provider = OpenJCEPlusFIPS
36+
RestrictedSecurity.Test-Profile-PolicySunset-Expired.securerandom.algorithm = SHA512DRBG
37+
38+
#
39+
# Test-Profile-PolicySunset-ExpireSoon
40+
#
41+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.desc.name = Test-Profile-PolicySunset-ExpireSoon
42+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.desc.default = true
43+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.desc.fips = true
44+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.desc.number = Certificate #XXX
45+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.desc.policy = https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/
46+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.desc.sunsetDate = 2023-09-21
47+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.fips.mode = 140-2
48+
49+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.jce.provider.1 = sun.security.provider.Sun
50+
51+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.securerandom.provider = OpenJCEPlusFIPS
52+
RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon.securerandom.algorithm = SHA512DRBG
53+
54+
#
55+
# Test-Profile-PolicySunset-NotExpire
56+
#
57+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.desc.name = Test-Profile-PolicySunset-NotExpire
58+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.desc.default = true
59+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.desc.fips = true
60+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.desc.hash = SHA256:706ce08e67373252aee08c6912f2994e3bbf4691b41ec8258325e37951bf96f3
61+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.desc.number = Certificate #XXX
62+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.desc.policy = https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/
63+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.desc.sunsetDate = 2045-09-21
64+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.fips.mode = 140-3
65+
66+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.jce.provider.1 = sun.security.provider.Sun
67+
68+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.securerandom.provider = OpenJCEPlusFIPS
69+
RestrictedSecurity.Test-Profile-PolicySunset-NotExpire.securerandom.algorithm = SHA512DRBG

0 commit comments

Comments
 (0)