Skip to content

Commit 779f6ce

Browse files
martinuygnu-andrew
authored andcommitted
RH2117972 - Extend the support for NSS DBs (PKCS11) in FIPS mode (#17)
Reviewed-by: @franferrax, @gnu-andrew
1 parent 1d4843f commit 779f6ce

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
@@ -310,6 +310,42 @@ keystore.type=pkcs12
310310
#
311311
fips.keystore.type=pkcs12
312312

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

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.*;
@@ -100,6 +101,8 @@ public final class SunPKCS11 extends AuthProvider {
100101
fipsExportKey = fipsExportKeyTmp;
101102
}
102103

104+
private static final String FIPS_NSSDB_PATH_PROP = "fips.nssdb.path";
105+
103106
static final Debug debug = Debug.getInstance("sunpkcs11");
104107
// the PKCS11 object through which we make the native calls
105108
@SuppressWarnings("serial") // Type of field is not Serializable;
@@ -158,6 +161,18 @@ public Provider configure(String configArg) throws InvalidParameterException {
158161
return AccessController.doPrivileged(new PrivilegedExceptionAction<>() {
159162
@Override
160163
public SunPKCS11 run() throws Exception {
164+
if (systemFipsEnabled) {
165+
/*
166+
* The nssSecmodDirectory attribute in the SunPKCS11
167+
* NSS configuration file takes the value of the
168+
* fips.nssdb.path System property after expansion.
169+
* Security properties expansion is unsupported.
170+
*/
171+
System.setProperty(
172+
FIPS_NSSDB_PATH_PROP,
173+
SecurityProperties.privilegedGetOverridable(
174+
FIPS_NSSDB_PATH_PROP));
175+
}
161176
return new SunPKCS11(new Config(newConfigName));
162177
}
163178
});
@@ -429,24 +444,6 @@ private static <T> T checkNull(T obj) {
429444
if (nssModule != null) {
430445
nssModule.setProvider(this);
431446
}
432-
if (systemFipsEnabled) {
433-
// The NSS Software Token in FIPS 140-2 mode requires a user
434-
// login for most operations. See sftk_fipsCheck. The NSS DB
435-
// (/etc/pki/nssdb) PIN is empty.
436-
Session session = null;
437-
try {
438-
session = token.getOpSession();
439-
p11.C_Login(session.id(), CKU_USER, new char[] {});
440-
} catch (PKCS11Exception p11e) {
441-
if (debug != null) {
442-
debug.println("Error during token login: " +
443-
p11e.getMessage());
444-
}
445-
throw p11e;
446-
} finally {
447-
token.releaseSession(session);
448-
}
449-
}
450447
} catch (Exception e) {
451448
if (config.getHandleStartupErrors() == Config.ERR_IGNORE_ALL) {
452449
throw new UnsupportedOperationException
@@ -1458,6 +1455,27 @@ public Object newInstance(Object param)
14581455
if (!token.isValid()) {
14591456
throw new NoSuchAlgorithmException("Token has been removed");
14601457
}
1458+
if (systemFipsEnabled && !token.fipsLoggedIn &&
1459+
!getType().equals("KeyStore")) {
1460+
/*
1461+
* The NSS Software Token in FIPS 140-2 mode requires a
1462+
* user login for most operations. See sftk_fipsCheck
1463+
* (nss/lib/softoken/fipstokn.c). In case of a KeyStore
1464+
* service, let the caller perform the login with
1465+
* KeyStore::load. Keytool, for example, does this to pass a
1466+
* PIN from either the -srcstorepass or -deststorepass
1467+
* argument. In case of a non-KeyStore service, perform the
1468+
* login now with the PIN available in the fips.nssdb.pin
1469+
* property.
1470+
*/
1471+
try {
1472+
token.ensureLoggedIn(null);
1473+
} catch (PKCS11Exception | LoginException e) {
1474+
throw new ProviderException("FIPS: error during the Token" +
1475+
" login required for the " + getType() +
1476+
" service.", e);
1477+
}
1478+
}
14611479
try {
14621480
return newInstance0(param);
14631481
} catch (PKCS11Exception e) {
@@ -1814,6 +1832,9 @@ public void logout() throws LoginException {
18141832
try {
18151833
session = token.getOpSession();
18161834
p11.C_Logout(session.id());
1835+
if (systemFipsEnabled) {
1836+
token.fipsLoggedIn = false;
1837+
}
18171838
if (debug != null) {
18181839
debug.println("logout succeeded");
18191840
}

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
final 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
@Serial
5357
private static final long serialVersionUID = 2541527649100571747L;
@@ -125,6 +129,10 @@ final class Token implements Serializable {
125129
// flag indicating whether we are logged in
126130
private volatile boolean loggedIn;
127131

132+
// Flag indicating the login status for the NSS Software Token in FIPS mode.
133+
// This Token is never asynchronously removed. Used from SunPKCS11.
134+
volatile boolean fipsLoggedIn;
135+
128136
// time we last checked login status
129137
private long lastLoginCheck;
130138

@@ -242,7 +250,12 @@ boolean isLoggedInNow(Session session) throws PKCS11Exception {
242250
// call provider.login() if not
243251
void ensureLoggedIn(Session session) throws PKCS11Exception, LoginException {
244252
if (!isLoggedIn(session)) {
245-
provider.login(null, null);
253+
if (systemFipsEnabled) {
254+
provider.login(null, new FIPSTokenLoginHandler());
255+
fipsLoggedIn = true;
256+
} else {
257+
provider.login(null, null);
258+
}
246259
}
247260
}
248261

0 commit comments

Comments
 (0)