Skip to content

Commit 40cac13

Browse files
authored
RH2117972 - Extend the support for NSS DBs (PKCS11) in FIPS mode (openjdk#17)
Reviewed-by: @franferrax, @gnu-andrew
1 parent f0a0bcc commit 40cac13

File tree

6 files changed

+589
-21
lines changed

6 files changed

+589
-21
lines changed

src/java.base/share/conf/security/java.security

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,42 @@ keystore.type=pkcs12
304304
#
305305
fips.keystore.type=pkcs12
306306

307+
#
308+
# Location of the NSS DB keystore (PKCS11) in FIPS mode.
309+
#
310+
# The syntax for this property is identical to the 'nssSecmodDirectory'
311+
# attribute available in the SunPKCS11 NSS configuration file. Use the
312+
# 'sql:' prefix to refer to an SQLite DB.
313+
#
314+
# If the system property fips.nssdb.path is also specified, it supersedes
315+
# the security property value defined here.
316+
#
317+
# Note: the default value for this property points to an NSS DB that might be
318+
# readable by multiple operating system users and unsuitable to store keys.
319+
#
320+
fips.nssdb.path=sql:/etc/pki/nssdb
321+
322+
#
323+
# PIN for the NSS DB keystore (PKCS11) in FIPS mode.
324+
#
325+
# Values must take any of the following forms:
326+
# 1) pin:<value>
327+
# Value: clear text PIN value.
328+
# 2) env:<value>
329+
# Value: environment variable containing the PIN value.
330+
# 3) file:<value>
331+
# Value: path to a file containing the PIN value in its first
332+
# line.
333+
#
334+
# If the system property fips.nssdb.pin is also specified, it supersedes
335+
# the security property value defined here.
336+
#
337+
# When used as a system property, UTF-8 encoded values are valid. When
338+
# used as a security property (such as in this file), encode non-Basic
339+
# Latin Unicode characters with \uXXXX.
340+
#
341+
fips.nssdb.pin=pin:
342+
307343
#
308344
# Controls compatibility mode for JKS and PKCS12 keystore types.
309345
#

src/java.base/share/conf/security/nss.fips.cfg.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = NSS-FIPS
22
nssLibraryDirectory = @NSS_LIBDIR@
3-
nssSecmodDirectory = sql:/etc/pki/nssdb
4-
nssDbMode = readOnly
3+
nssSecmodDirectory = ${fips.nssdb.path}
4+
nssDbMode = readWrite
55
nssModule = fips
66

77
attributes(*,CKO_SECRET_KEY,CKK_GENERIC_SECRET)={ CKA_SIGN=true }
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright (c) 2022, Red Hat, Inc.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle 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, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package sun.security.pkcs11;
27+
28+
import java.io.BufferedReader;
29+
import java.io.ByteArrayInputStream;
30+
import java.io.InputStream;
31+
import java.io.InputStreamReader;
32+
import java.io.IOException;
33+
import java.nio.charset.StandardCharsets;
34+
import java.nio.file.Files;
35+
import java.nio.file.Path;
36+
import java.nio.file.Paths;
37+
import java.nio.file.StandardOpenOption;
38+
import java.security.ProviderException;
39+
40+
import javax.security.auth.callback.Callback;
41+
import javax.security.auth.callback.CallbackHandler;
42+
import javax.security.auth.callback.PasswordCallback;
43+
import javax.security.auth.callback.UnsupportedCallbackException;
44+
45+
import sun.security.util.Debug;
46+
import sun.security.util.SecurityProperties;
47+
48+
final class FIPSTokenLoginHandler implements CallbackHandler {
49+
50+
private static final String FIPS_NSSDB_PIN_PROP = "fips.nssdb.pin";
51+
52+
private static final Debug debug = Debug.getInstance("sunpkcs11");
53+
54+
public void handle(Callback[] callbacks)
55+
throws IOException, UnsupportedCallbackException {
56+
if (!(callbacks[0] instanceof PasswordCallback)) {
57+
throw new UnsupportedCallbackException(callbacks[0]);
58+
}
59+
PasswordCallback pc = (PasswordCallback)callbacks[0];
60+
pc.setPassword(getFipsNssdbPin());
61+
}
62+
63+
private static char[] getFipsNssdbPin() throws ProviderException {
64+
if (debug != null) {
65+
debug.println("FIPS: Reading NSS DB PIN for token...");
66+
}
67+
String pinProp = SecurityProperties
68+
.privilegedGetOverridable(FIPS_NSSDB_PIN_PROP);
69+
if (pinProp != null && !pinProp.isEmpty()) {
70+
String[] pinPropParts = pinProp.split(":", 2);
71+
if (pinPropParts.length < 2) {
72+
throw new ProviderException("Invalid " + FIPS_NSSDB_PIN_PROP +
73+
" property value.");
74+
}
75+
String prefix = pinPropParts[0].toLowerCase();
76+
String value = pinPropParts[1];
77+
String pin = null;
78+
if (prefix.equals("env")) {
79+
if (debug != null) {
80+
debug.println("FIPS: PIN value from the '" + value +
81+
"' environment variable.");
82+
}
83+
pin = System.getenv(value);
84+
} else if (prefix.equals("file")) {
85+
if (debug != null) {
86+
debug.println("FIPS: PIN value from the '" + value +
87+
"' file.");
88+
}
89+
pin = getPinFromFile(Paths.get(value));
90+
} else if (prefix.equals("pin")) {
91+
if (debug != null) {
92+
debug.println("FIPS: PIN value from the " +
93+
FIPS_NSSDB_PIN_PROP + " property.");
94+
}
95+
pin = value;
96+
} else {
97+
throw new ProviderException("Unsupported prefix for " +
98+
FIPS_NSSDB_PIN_PROP + ".");
99+
}
100+
if (pin != null && !pin.isEmpty()) {
101+
if (debug != null) {
102+
debug.println("FIPS: non-empty PIN.");
103+
}
104+
/*
105+
* C_Login in libj2pkcs11 receives the PIN in a char[] and
106+
* discards the upper byte of each char, before passing
107+
* the value to the NSS Software Token. However, the
108+
* NSS Software Token accepts any UTF-8 PIN value. Thus,
109+
* expand the PIN here to account for later truncation.
110+
*/
111+
byte[] pinUtf8 = pin.getBytes(StandardCharsets.UTF_8);
112+
char[] pinChar = new char[pinUtf8.length];
113+
for (int i = 0; i < pinChar.length; i++) {
114+
pinChar[i] = (char)(pinUtf8[i] & 0xFF);
115+
}
116+
return pinChar;
117+
}
118+
}
119+
if (debug != null) {
120+
debug.println("FIPS: empty PIN.");
121+
}
122+
return null;
123+
}
124+
125+
/*
126+
* This method extracts the token PIN from the first line of a password
127+
* file in the same way as NSS modutil. See for example the -newpwfile
128+
* argument used to change the password for an NSS DB.
129+
*/
130+
private static String getPinFromFile(Path f) throws ProviderException {
131+
try (InputStream is =
132+
Files.newInputStream(f, StandardOpenOption.READ)) {
133+
/*
134+
* SECU_FilePasswd in NSS (nss/cmd/lib/secutil.c), used by modutil,
135+
* reads up to 4096 bytes. In addition, the NSS Software Token
136+
* does not accept PINs longer than 500 bytes (see SFTK_MAX_PIN
137+
* in nss/lib/softoken/pkcs11i.h).
138+
*/
139+
BufferedReader in =
140+
new BufferedReader(new InputStreamReader(
141+
new ByteArrayInputStream(is.readNBytes(4096)),
142+
StandardCharsets.UTF_8));
143+
return in.readLine();
144+
} catch (IOException ioe) {
145+
throw new ProviderException("Error reading " + FIPS_NSSDB_PIN_PROP +
146+
" from the '" + f + "' file.", ioe);
147+
}
148+
}
149+
}

src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/SunPKCS11.java

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import sun.security.util.Debug;
5151
import sun.security.util.ResourcesMgr;
5252
import static sun.security.util.SecurityConstants.PROVIDER_VER;
53+
import sun.security.util.SecurityProperties;
5354
import static sun.security.util.SecurityProviderConstants.getAliases;
5455

5556
import sun.security.pkcs11.Secmod.*;
@@ -97,6 +98,8 @@ public final class SunPKCS11 extends AuthProvider {
9798
fipsExportKey = fipsExportKeyTmp;
9899
}
99100

101+
private static final String FIPS_NSSDB_PATH_PROP = "fips.nssdb.path";
102+
100103
private static final long serialVersionUID = -1354835039035306505L;
101104

102105
static final Debug debug = Debug.getInstance("sunpkcs11");
@@ -150,6 +153,18 @@ public Provider configure(String configArg) throws InvalidParameterException {
150153
return AccessController.doPrivileged(new PrivilegedExceptionAction<>() {
151154
@Override
152155
public SunPKCS11 run() throws Exception {
156+
if (systemFipsEnabled) {
157+
/*
158+
* The nssSecmodDirectory attribute in the SunPKCS11
159+
* NSS configuration file takes the value of the
160+
* fips.nssdb.path System property after expansion.
161+
* Security properties expansion is unsupported.
162+
*/
163+
System.setProperty(
164+
FIPS_NSSDB_PATH_PROP,
165+
SecurityProperties.privilegedGetOverridable(
166+
FIPS_NSSDB_PATH_PROP));
167+
}
153168
return new SunPKCS11(new Config(newConfigName));
154169
}
155170
});
@@ -424,24 +439,6 @@ private static <T> T checkNull(T obj) {
424439
if (nssModule != null) {
425440
nssModule.setProvider(this);
426441
}
427-
if (systemFipsEnabled) {
428-
// The NSS Software Token in FIPS 140-2 mode requires a user
429-
// login for most operations. See sftk_fipsCheck. The NSS DB
430-
// (/etc/pki/nssdb) PIN is empty.
431-
Session session = null;
432-
try {
433-
session = token.getOpSession();
434-
p11.C_Login(session.id(), CKU_USER, new char[] {});
435-
} catch (PKCS11Exception p11e) {
436-
if (debug != null) {
437-
debug.println("Error during token login: " +
438-
p11e.getMessage());
439-
}
440-
throw p11e;
441-
} finally {
442-
token.releaseSession(session);
443-
}
444-
}
445442
} catch (Exception e) {
446443
if (config.getHandleStartupErrors() == Config.ERR_IGNORE_ALL) {
447444
throw new UnsupportedOperationException
@@ -1441,6 +1438,27 @@ public Object newInstance(Object param)
14411438
if (token.isValid() == false) {
14421439
throw new NoSuchAlgorithmException("Token has been removed");
14431440
}
1441+
if (systemFipsEnabled && !token.fipsLoggedIn &&
1442+
!getType().equals("KeyStore")) {
1443+
/*
1444+
* The NSS Software Token in FIPS 140-2 mode requires a
1445+
* user login for most operations. See sftk_fipsCheck
1446+
* (nss/lib/softoken/fipstokn.c). In case of a KeyStore
1447+
* service, let the caller perform the login with
1448+
* KeyStore::load. Keytool, for example, does this to pass a
1449+
* PIN from either the -srcstorepass or -deststorepass
1450+
* argument. In case of a non-KeyStore service, perform the
1451+
* login now with the PIN available in the fips.nssdb.pin
1452+
* property.
1453+
*/
1454+
try {
1455+
token.ensureLoggedIn(null);
1456+
} catch (PKCS11Exception | LoginException e) {
1457+
throw new ProviderException("FIPS: error during the Token" +
1458+
" login required for the " + getType() +
1459+
" service.", e);
1460+
}
1461+
}
14441462
try {
14451463
return newInstance0(param);
14461464
} catch (PKCS11Exception e) {
@@ -1797,6 +1815,9 @@ public void logout() throws LoginException {
17971815
try {
17981816
session = token.getOpSession();
17991817
p11.C_Logout(session.id());
1818+
if (systemFipsEnabled) {
1819+
token.fipsLoggedIn = false;
1820+
}
18001821
if (debug != null) {
18011822
debug.println("logout succeeded");
18021823
}

src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Token.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.security.*;
3434
import javax.security.auth.login.LoginException;
3535

36+
import jdk.internal.access.SharedSecrets;
3637
import sun.security.jca.JCAUtil;
3738

3839
import sun.security.pkcs11.wrapper.*;
@@ -48,6 +49,9 @@
4849
*/
4950
class Token implements Serializable {
5051

52+
private static final boolean systemFipsEnabled = SharedSecrets
53+
.getJavaSecuritySystemConfiguratorAccess().isSystemFipsEnabled();
54+
5155
// need to be serializable to allow SecureRandom to be serialized
5256
private static final long serialVersionUID = 2541527649100571747L;
5357

@@ -114,6 +118,10 @@ class Token implements Serializable {
114118
// flag indicating whether we are logged in
115119
private volatile boolean loggedIn;
116120

121+
// Flag indicating the login status for the NSS Software Token in FIPS mode.
122+
// This Token is never asynchronously removed. Used from SunPKCS11.
123+
volatile boolean fipsLoggedIn;
124+
117125
// time we last checked login status
118126
private long lastLoginCheck;
119127

@@ -232,7 +240,12 @@ boolean isLoggedInNow(Session session) throws PKCS11Exception {
232240
// call provider.login() if not
233241
void ensureLoggedIn(Session session) throws PKCS11Exception, LoginException {
234242
if (isLoggedIn(session) == false) {
235-
provider.login(null, null);
243+
if (systemFipsEnabled) {
244+
provider.login(null, new FIPSTokenLoginHandler());
245+
fipsLoggedIn = true;
246+
} else {
247+
provider.login(null, null);
248+
}
236249
}
237250
}
238251

0 commit comments

Comments
 (0)