Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RH2117972 - Extend the support for NSS DBs (PKCS11) in FIPS mode #3

Open
wants to merge 7 commits into
base: fips
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions jdk/src/share/classes/sun/security/pkcs11/FIPSTokenLoginHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Copyright (c) 2022, Red Hat, Inc.
* 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.security.pkcs11;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.ProviderException;

import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;

import sun.misc.IOUtils;
import sun.security.util.Debug;
import sun.security.util.SecurityProperties;

final class FIPSTokenLoginHandler implements CallbackHandler {

private static final String FIPS_NSSDB_PIN_PROP = "fips.nssdb.pin";

private static final Debug debug = Debug.getInstance("sunpkcs11");

public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
if (!(callbacks[0] instanceof PasswordCallback)) {
throw new UnsupportedCallbackException(callbacks[0]);
}
PasswordCallback pc = (PasswordCallback)callbacks[0];
pc.setPassword(getFipsNssdbPin());
}

private static char[] getFipsNssdbPin() throws ProviderException {
if (debug != null) {
debug.println("FIPS: Reading NSS DB PIN for token...");
}
String pinProp = SecurityProperties
.privilegedGetOverridable(FIPS_NSSDB_PIN_PROP);
if (pinProp != null && !pinProp.isEmpty()) {
String[] pinPropParts = pinProp.split(":", 2);
if (pinPropParts.length < 2) {
throw new ProviderException("Invalid " + FIPS_NSSDB_PIN_PROP +
" property value.");
}
String prefix = pinPropParts[0].toUpperCase();
String value = pinPropParts[1];
String pin = null;
if (prefix.equals("ENV")) {
if (debug != null) {
debug.println("FIPS: PIN value from the '" + value +
"' environment variable.");
}
pin = System.getenv(value);
} else if (prefix.equals("FILE")) {
if (debug != null) {
debug.println("FIPS: PIN value from the '" + value +
"' file.");
}
pin = getPinFromFile(Paths.get(value));
} else if (prefix.equals("PIN")) {
if (debug != null) {
debug.println("FIPS: PIN value from the " +
FIPS_NSSDB_PIN_PROP + " property.");
}
pin = value;
} else {
throw new ProviderException("Unsupported prefix for " +
FIPS_NSSDB_PIN_PROP + ".");
}
if (pin != null && !pin.isEmpty()) {
if (debug != null) {
debug.println("FIPS: non-empty PIN.");
}
/*
* C_Login in libj2pkcs11 receives the PIN in a char[] and
* discards the upper byte of each char, before passing
* the value to the NSS Software Token. However, the
* NSS Software Token accepts any UTF-8 PIN value. Thus,
* expand the PIN here to account for later truncation.
*/
byte[] pinUtf8 = pin.getBytes(StandardCharsets.UTF_8);
char[] pinChar = new char[pinUtf8.length];
for (int i = 0; i < pinChar.length; i++) {
pinChar[i] = (char)(pinUtf8[i] & 0xFF);
}
return pinChar;
}
}
if (debug != null) {
debug.println("FIPS: empty PIN.");
}
return new char[] {};
}

/*
* This method extracts the token PIN from the first line of a password
* file in the same way as NSS modutil. See for example the -newpwfile
* argument used to change the password for an NSS DB.
*/
private static String getPinFromFile(Path f) throws ProviderException {
try (InputStream is =
Files.newInputStream(f, StandardOpenOption.READ)) {
/*
* SECU_FilePasswd in NSS (nss/cmd/lib/secutil.c), used by modutil,
* reads up to 4096 bytes. In addition, the NSS Software Token
* does not accept PINs longer than 500 bytes (see SFTK_MAX_PIN
* in nss/lib/softoken/pkcs11i.h).
*/
BufferedReader in =
new BufferedReader(new InputStreamReader(
new ByteArrayInputStream(IOUtils.readNBytes(is, 4096)),
martinuy marked this conversation as resolved.
Show resolved Hide resolved
StandardCharsets.UTF_8));
return in.readLine();
} catch (IOException ioe) {
throw new ProviderException("Error reading " + FIPS_NSSDB_PIN_PROP +
" from the '" + f + "' file.", ioe);
}
}
}
2 changes: 1 addition & 1 deletion jdk/src/share/classes/sun/security/pkcs11/Secmod.java
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public synchronized void initialize(DbMode dbMode, String configDir,

if (configDir != null) {
String configDirPath = null;
String sqlPrefix = "sql:/";
String sqlPrefix = "sql:";
if (!configDir.startsWith(sqlPrefix)) {
configDirPath = configDir;
} else {
Expand Down
63 changes: 44 additions & 19 deletions jdk/src/share/classes/sun/security/pkcs11/SunPKCS11.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

import sun.security.util.Debug;
import sun.security.util.ResourcesMgr;
import sun.security.util.SecurityProperties;

import sun.security.pkcs11.Secmod.*;

Expand Down Expand Up @@ -86,6 +87,8 @@ public final class SunPKCS11 extends AuthProvider {
fipsImportKey = fipsImportKeyTmp;
}

private static final String FIPS_NSSDB_PATH_PROP = "fips.nssdb.path";

private static final long serialVersionUID = -1354835039035306505L;

static final Debug debug = Debug.getInstance("sunpkcs11");
Expand Down Expand Up @@ -147,13 +150,29 @@ private static synchronized String getDummyConfigName() {
return "---DummyConfig-" + id + "---";
}

private static String fipsPreConfigHook() {
if (systemFipsEnabled) {
/*
* The nssSecmodDirectory attribute in the SunPKCS11
* NSS configuration file takes the value of the
* fips.nssdb.path System property after expansion.
* Security properties expansion is unsupported.
*/
System.setProperty(
FIPS_NSSDB_PATH_PROP,
SecurityProperties.privilegedGetOverridable(
FIPS_NSSDB_PATH_PROP));
}
return "";
}

/**
* @deprecated use new SunPKCS11(String) or new SunPKCS11(InputStream)
* instead
*/
@Deprecated
public SunPKCS11(String configName, InputStream configStream) {
super("SunPKCS11-" +
super(fipsPreConfigHook() + "SunPKCS11-" +
Config.getConfig(configName, configStream).getName(),
1.8d, Config.getConfig(configName, configStream).getDescription());
this.configName = configName;
Expand Down Expand Up @@ -401,24 +420,6 @@ public SunPKCS11(String configName, InputStream configStream) {
if (nssModule != null) {
nssModule.setProvider(this);
}
if (systemFipsEnabled) {
// The NSS Software Token in FIPS 140-2 mode requires a user
// login for most operations. See sftk_fipsCheck. The NSS DB
// (/etc/pki/nssdb) PIN is empty.
Session session = null;
try {
session = token.getOpSession();
p11.C_Login(session.id(), CKU_USER, new char[] {});
} catch (PKCS11Exception p11e) {
if (debug != null) {
debug.println("Error during token login: " +
p11e.getMessage());
}
throw p11e;
} finally {
token.releaseSession(session);
}
}
} catch (Exception e) {
if (config.getHandleStartupErrors() == Config.ERR_IGNORE_ALL) {
throw new UnsupportedOperationException
Expand Down Expand Up @@ -1106,6 +1107,27 @@ public Object newInstance(Object param)
if (token.isValid() == false) {
throw new NoSuchAlgorithmException("Token has been removed");
}
if (systemFipsEnabled && !token.fipsLoggedIn &&
!getType().equals("KeyStore")) {
/*
* The NSS Software Token in FIPS 140-2 mode requires a
* user login for most operations. See sftk_fipsCheck
* (nss/lib/softoken/fipstokn.c). In case of a KeyStore
* service, let the caller perform the login with
* KeyStore::load. Keytool, for example, does this to pass a
* PIN from either the -srcstorepass or -deststorepass
* argument. In case of a non-KeyStore service, perform the
* login now with the PIN available in the fips.nssdb.pin
* property.
*/
try {
token.ensureLoggedIn(null);
} catch (PKCS11Exception | LoginException e) {
throw new ProviderException("FIPS: error during the Token" +
" login required for the " + getType() +
" service.", e);
}
}
try {
return newInstance0(param);
} catch (PKCS11Exception e) {
Expand Down Expand Up @@ -1444,6 +1466,9 @@ public void logout() throws LoginException {
try {
session = token.getOpSession();
p11.C_Logout(session.id());
if (systemFipsEnabled) {
token.fipsLoggedIn = false;
}
if (debug != null) {
debug.println("logout succeeded");
}
Expand Down
15 changes: 14 additions & 1 deletion jdk/src/share/classes/sun/security/pkcs11/Token.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.security.*;
import javax.security.auth.login.LoginException;

import sun.misc.SharedSecrets;
import sun.security.jca.JCAUtil;

import sun.security.pkcs11.wrapper.*;
Expand All @@ -47,6 +48,9 @@
*/
class Token implements Serializable {

private static final boolean systemFipsEnabled = SharedSecrets
.getJavaSecuritySystemConfiguratorAccess().isSystemFipsEnabled();

// need to be serializable to allow SecureRandom to be serialized
private static final long serialVersionUID = 2541527649100571747L;

Expand Down Expand Up @@ -113,6 +117,10 @@ class Token implements Serializable {
// flag indicating whether we are logged in
private volatile boolean loggedIn;

// Flag indicating the login status for the NSS Software Token in FIPS mode.
// This Token is never asynchronously removed. Used from SunPKCS11.
volatile boolean fipsLoggedIn;

// time we last checked login status
private long lastLoginCheck;

Expand Down Expand Up @@ -231,7 +239,12 @@ boolean isLoggedInNow(Session session) throws PKCS11Exception {
// call provider.login() if not
void ensureLoggedIn(Session session) throws PKCS11Exception, LoginException {
if (isLoggedIn(session) == false) {
provider.login(null, null);
if (systemFipsEnabled) {
provider.login(null, new FIPSTokenLoginHandler());
fipsLoggedIn = true;
} else {
provider.login(null, null);
}
}
}

Expand Down
36 changes: 36 additions & 0 deletions jdk/src/share/lib/security/java.security-linux
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,42 @@ keystore.type=jks
#
fips.keystore.type=PKCS11

#
# Location of the NSS DB keystore (PKCS11) in FIPS mode.
#
# The syntax for this property is identical to the 'nssSecmodDirectory'
# attribute available in the SunPKCS11 NSS configuration file. Use the
# 'sql:' prefix to refer to an SQLite DB.
#
# If the system property fips.nssdb.path is also specified, it supersedes
# the security property value defined here.
#
# Note: the default value for this property points to an NSS DB that might be
# readable by multiple operating system users and unsuitable to store keys.
#
fips.nssdb.path=sql:/etc/pki/nssdb

#
# PIN for the NSS DB keystore (PKCS11) in FIPS mode.
#
# Values must take any of the following forms:
# 1) pin:<value>
# Value: clear text PIN value.
# 2) env:<value>
# Value: environment variable containing the PIN value.
# 3) file:<value>
# Value: path to a file containing the PIN value in its first
# line.
#
# If the system property fips.nssdb.pin is also specified, it supersedes
# the security property value defined here.
#
# When used as a system property, UTF-8 encoded values are valid. When
# used as a security property (such as in this file), encode non-Basic
# Latin Unicode characters with \uXXXX.
#
fips.nssdb.pin=pin:

#
# Controls compatibility mode for the JKS keystore type.
#
Expand Down
Loading