Skip to content

Commit 3229c52

Browse files
committed
Implement sunset policy handling with suppress and ignore options
Added logic to handle restricted security profile sunset behavior using -Dsemeru.restrictedsecurity.suppresssunsetwarning and -Dsemeru.restrictedsecurity.ignoresunsetexpiration system properties. When suppresssunsetwarning is true, all sunset warning messages are suppressed; if the profile is expired and ignoresunsetexpiration is false, the JVM exits silently with status 1. When suppresssunsetwarning is false. If the profile has expired and ignoresunsetexpiration is false, a fatal error is printed and the JVM terminates. If expired and ignoresunsetexpiration is 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 f6c729c commit 3229c52

File tree

4 files changed

+290
-19
lines changed

4 files changed

+290
-19
lines changed

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

Lines changed: 60 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;
@@ -63,6 +64,8 @@ public final class RestrictedSecurity {
6364
private static boolean isFIPSEnabled;
6465

6566
private static final boolean allowSetProperties;
67+
private static final boolean suppressSunsetWarning;
68+
private static final boolean ignoreSunsetExpiration;
6669

6770
private static final boolean isNSSSupported;
6871
private static final boolean isOpenJCEPlusCertifiedPlatform;
@@ -104,6 +107,8 @@ public final class RestrictedSecurity {
104107

105108
userEnabledFIPS = Boolean.getBoolean("semeru.fips");
106109
allowSetProperties = Boolean.getBoolean("semeru.fips.allowsetproperties");
110+
suppressSunsetWarning = Boolean.getBoolean("semeru.restrictedsecurity.suppresssunsetwarning");
111+
ignoreSunsetExpiration = Boolean.getBoolean("semeru.restrictedsecurity.ignoresunsetexpiration");
107112

108113
if (userEnabledFIPS) {
109114
if (isFIPSSupported) {
@@ -612,9 +617,36 @@ private static void restrictsCheck() {
612617
printStackTraceAndExit("Restricted security property is null.");
613618
}
614619

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

620652
// Check secure random settings.
@@ -633,25 +665,35 @@ private static void restrictsCheck() {
633665
* Check if restricted security policy is sunset.
634666
*
635667
* @param descSunsetDate the sunset date from java.security
636-
* @return true if restricted security policy sunset
668+
* @return the number of months until restricted security policy sunset
637669
*/
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-
}
670+
private static int monthsToPolicySunset(String descSunsetDate) {
671+
// If no sunset date is specified, it will not sunset.
672+
if (isNullOrBlank(descSunsetDate)) {
673+
return Integer.MAX_VALUE;
649674
}
650675

651-
if (debug != null) {
652-
debug.println("Restricted security policy is sunset: " + isSunset);
676+
LocalDate sunsetDate;
677+
try {
678+
sunsetDate = LocalDate.parse(descSunsetDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
679+
} catch (DateTimeParseException e) {
680+
printStackTraceAndExit(
681+
"Restricted security policy sunset date is incorrect, the correct format is yyyy-MM-dd.");
682+
return 0;
683+
}
684+
685+
LocalDate now = LocalDate.now(Clock.systemUTC());
686+
687+
if (sunsetDate.isBefore(now)) {
688+
return 0; // Already sunset.
689+
}
690+
691+
Period period = Period.between(now, sunsetDate);
692+
int months = (period.getYears() * 12) + period.getMonths();
693+
if (period.getDays() > 0) {
694+
months += 1;
653695
}
654-
return isSunset;
696+
return months;
655697
}
656698

657699
/**
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
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.Clock;
44+
import java.time.LocalDate;
45+
import java.time.ZoneOffset;
46+
import java.time.format.DateTimeFormatter;
47+
import java.util.stream.Stream;
48+
49+
import jdk.test.lib.process.OutputAnalyzer;
50+
import jdk.test.lib.process.ProcessTools;
51+
52+
public class TestPolicySunset {
53+
54+
private static Path updateExpireSoonSunsetFile(String baseFile) {
55+
try {
56+
LocalDate soonDate = LocalDate.now(Clock.systemUTC()).plusMonths(1);
57+
String newDate = soonDate.format(DateTimeFormatter.ISO_DATE);
58+
59+
String content = Files.readString(Paths.get(baseFile), StandardCharsets.UTF_8);
60+
String pattern = "(?m)^(RestrictedSecurity\\.Test-Profile-PolicySunset-ExpireSoon\\.desc\\.sunsetDate)\\s*=.*$";
61+
String updated = content.replaceAll(pattern, "$1 = " + newDate);
62+
63+
Path tmp = Files.createTempFile("sunset-java.security.expireSoon.", ".tmp");
64+
Files.writeString(tmp, updated, StandardCharsets.UTF_8);
65+
return tmp;
66+
} catch (IOException e) {
67+
throw new RuntimeException("Failed to update sunset date for ExpireSoon profile", e);
68+
}
69+
}
70+
71+
private static Stream<Arguments> patternMatches_expectedExitValue0() {
72+
String propertyFile = System.getProperty("test.src") + "/sunset-java.security";
73+
String updatedPropertyFile = updateExpireSoonSunsetFile(propertyFile).toString();
74+
75+
return Stream.of(
76+
// 1 - expired; supress=false; ignore=true
77+
Arguments.of("Test-Profile-PolicySunset-Expired",
78+
propertyFile,
79+
"false", "true",
80+
"WARNING: java will start with the requested restricted security profile but uncertified cryptography may be active"),
81+
// 2 - expired; supress=true; ignore=true, no warning
82+
Arguments.of("Test-Profile-PolicySunset-Expired",
83+
propertyFile,
84+
"true", "true",
85+
""),
86+
// 3 - expire soon (<=6 months); supress=false
87+
Arguments.of("Test-Profile-PolicySunset-ExpireSoon",
88+
updatedPropertyFile,
89+
"false", "false",
90+
"The restricted security profile RestrictedSecurity.Test-Profile-PolicySunset-ExpireSoon will expire"),
91+
// 4 - expire soon (<=6 months); supress=true, no warning
92+
Arguments.of("Test-Profile-PolicySunset-ExpireSoon",
93+
updatedPropertyFile,
94+
"true", "false",
95+
""),
96+
// 5 - not expire (>6 months); no warning
97+
Arguments.of("Test-Profile-PolicySunset-NotExpire",
98+
propertyFile,
99+
"false", "false",
100+
""));
101+
}
102+
103+
private static Stream<Arguments> patternMatches_expectedExitValue1() {
104+
String propertyFile = System.getProperty("test.src") + "/sunset-java.security";
105+
106+
return Stream.of(
107+
// 1 - expired; supress=false; ignore=false
108+
Arguments.of("Test-Profile-PolicySunset-Expired",
109+
propertyFile,
110+
"false", "false",
111+
"Use the -Dsemeru.restrictedsecurity.ignoresunsetexpiration to allow java to start while possibly using uncertified cryptography"),
112+
// 2 - expired; supress=true; ignore=false, no warning
113+
Arguments.of("Test-Profile-PolicySunset-Expired",
114+
propertyFile,
115+
"true", "false",
116+
""));
117+
}
118+
119+
@ParameterizedTest
120+
@MethodSource("patternMatches_expectedExitValue0")
121+
public void shouldContain_expectedExitValue0(String customprofile, String securityPropertyFile, String suppresssunsetwarning, String ignoresunsetexpiration, String expected) throws Exception {
122+
OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(
123+
"-Dsemeru.fips=true",
124+
"-Dsemeru.customprofile=" + customprofile,
125+
"-Djava.security.properties=" + securityPropertyFile,
126+
"-Dsemeru.restrictedsecurity.suppresssunsetwarning=" + suppresssunsetwarning,
127+
"-Dsemeru.restrictedsecurity.ignoresunsetexpiration=" + ignoresunsetexpiration,
128+
"TestPolicySunset"
129+
);
130+
outputAnalyzer.reportDiagnosticSummary();
131+
outputAnalyzer.shouldHaveExitValue(0).shouldMatch(expected);
132+
}
133+
134+
@ParameterizedTest
135+
@MethodSource("patternMatches_expectedExitValue1")
136+
public void shouldContain_expectedExitValue1(String customprofile, String securityPropertyFile, String suppresssunsetwarning, String ignoresunsetexpiration, String expected) throws Exception {
137+
OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(
138+
"-Dsemeru.fips=true",
139+
"-Dsemeru.customprofile=" + customprofile,
140+
"-Djava.security.properties=" + securityPropertyFile,
141+
"-Dsemeru.restrictedsecurity.suppresssunsetwarning=" + suppresssunsetwarning,
142+
"-Dsemeru.restrictedsecurity.ignoresunsetexpiration=" + ignoresunsetexpiration,
143+
"TestPolicySunset"
144+
);
145+
outputAnalyzer.reportDiagnosticSummary();
146+
outputAnalyzer.shouldHaveExitValue(1).shouldMatch(expected);
147+
}
148+
149+
public static void main(String[] args) {
150+
// Something to trigger "properties" debug output.
151+
try {
152+
for (Provider provider : Security.getProviders()) {
153+
System.out.println("Provider Name: " + provider.getName());
154+
System.out.println("Provider Version: " + provider.getVersionStr());
155+
}
156+
} catch (Exception e) {
157+
System.out.println(e);
158+
}
159+
}
160+
}

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)