diff --git a/zookeeper-client/zookeeper-client-c/tests/zkServer.sh b/zookeeper-client/zookeeper-client-c/tests/zkServer.sh index 7505bf3a8f7..c7dd9bb1039 100755 --- a/zookeeper-client/zookeeper-client-c/tests/zkServer.sh +++ b/zookeeper-client/zookeeper-client-c/tests/zkServer.sh @@ -67,7 +67,7 @@ rm -f "${base_dir}/build/tmp/zk.pid" fi # [ZOOKEEPER-820] If lsof command is present, look for a process listening -# on ZOOPORT and kill it. +# on ZOOPORT and kill it. which lsof &> /dev/null if [ $? -eq 0 ] then @@ -129,6 +129,7 @@ if [ "x$1" == "xstartRequireSASLAuth" ] then PROPERTIES="-Dzookeeper.sessionRequireClientSASLAuth=true $PROPERTIES" PROPERTIES="$PROPERTIES -Dzookeeper.authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider" + PROPERTIES="$PROPERTIES -Dzookeeper.fips-mode=false" if [ "x$2" != "x" ] then PROPERTIES="$PROPERTIES -Djava.security.auth.login.config=$2" diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperSaslServer.java b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperSaslServer.java index bf5089d792b..98c8d0e6bb5 100644 --- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperSaslServer.java +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/ZooKeeperSaslServer.java @@ -22,6 +22,7 @@ import javax.security.sasl.SaslException; import javax.security.sasl.SaslServer; import org.apache.zookeeper.Login; +import org.apache.zookeeper.common.ZKConfig; import org.apache.zookeeper.util.SecurityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,20 +33,26 @@ public class ZooKeeperSaslServer { public static final String DEFAULT_LOGIN_CONTEXT_NAME = "Server"; private static final Logger LOG = LoggerFactory.getLogger(ZooKeeperSaslServer.class); - private SaslServer saslServer; + private final SaslServer saslServer; + private final ZKConfig config; ZooKeeperSaslServer(final Login login) { + config = new ZKConfig(); saslServer = createSaslServer(login); } private SaslServer createSaslServer(final Login login) { synchronized (login) { Subject subject = login.getSubject(); - return SecurityUtils.createSaslServer(subject, "zookeeper", "zk-sasl-md5", login.newCallbackHandler(), LOG); + return SecurityUtils.createSaslServer(config, subject, "zookeeper", "zk-sasl-md5", login.newCallbackHandler(), LOG); } } public byte[] evaluateResponse(byte[] response) throws SaslException { + if (saslServer == null) { + LOG.error("SaslServer failed to initialize (FIPS mode may have blocked DIGEST-MD5). Cannot authenticate client."); + throw new SaslException("SaslServer is null, cannot evaluate client SASL response."); + } return saslServer.evaluateResponse(response); } diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java index a1425833b7f..ca651115d0a 100644 --- a/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/server/quorum/auth/SaslQuorumAuthServer.java @@ -47,6 +47,7 @@ public class SaslQuorumAuthServer implements QuorumAuthServer { private static final int MAX_RETRIES = 5; private final Login serverLogin; private final boolean quorumRequireSasl; + private final ZKConfig zkConfig; public SaslQuorumAuthServer(boolean quorumRequireSasl, String loginContext, Set authzHosts) throws SaslException { this.quorumRequireSasl = quorumRequireSasl; @@ -60,7 +61,8 @@ public SaslQuorumAuthServer(boolean quorumRequireSasl, String loginContext, Set< Supplier callbackSupplier = () -> { return new SaslQuorumServerCallbackHandler(entries, authzHosts); }; - serverLogin = new Login(loginContext, callbackSupplier, new ZKConfig()); + zkConfig = new ZKConfig(); + serverLogin = new Login(loginContext, callbackSupplier, zkConfig); serverLogin.startThreadIfNeeded(); } catch (Throwable e) { throw new SaslException("Failed to initialize authentication mechanism using SASL", e); @@ -86,11 +88,15 @@ public void authenticate(Socket sock, DataInputStream din) throws SaslException dout = new DataOutputStream(sock.getOutputStream()); byte[] challenge = null; ss = SecurityUtils.createSaslServer( + zkConfig, serverLogin.getSubject(), QuorumAuth.QUORUM_SERVER_PROTOCOL_NAME, QuorumAuth.QUORUM_SERVER_SASL_DIGEST, serverLogin.newCallbackHandler(), LOG); + if (ss == null) { + throw new SaslException("Failed to create SaslServer (FIPS mode may be blocking DIGEST-MD5)"); + } while (!ss.isComplete()) { challenge = ss.evaluateResponse(token); if (!ss.isComplete()) { diff --git a/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java b/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java index 5c44f21161f..e29dabcf01a 100644 --- a/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java +++ b/zookeeper-server/src/main/java/org/apache/zookeeper/util/SecurityUtils.java @@ -153,6 +153,7 @@ public SaslClient run() throws SaslException { /** * Create an instance of a SaslServer. It will return null if there is an exception. * + * @param config to check whether FIPS mode is enabled. * @param subject subject * @param protocol protocol * @param serverName server name @@ -161,6 +162,7 @@ public SaslClient run() throws SaslException { * @return sasl server object */ public static SaslServer createSaslServer( + final ZKConfig config, final Subject subject, final String protocol, final String serverName, @@ -251,6 +253,11 @@ public SaslServer run() { // JAAS non-GSSAPI authentication: assuming and supporting only // DIGEST-MD5 mechanism for now. // TODO: use 'authMech=' value in zoo.cfg. + // FIPS-mode: don't try DIGEST-MD5, just return error + if (X509Util.getFipsMode(config)) { + LOG.warn("SaslServer will not use DIGEST-MD5 as SASL mechanism, because FIPS mode is enabled."); + return null; + } try { SaslServer saslServer = Sasl.createSaslServer("DIGEST-MD5", protocol, serverName, null, callbackHandler); return saslServer; diff --git a/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthFailFipsModeTest.java b/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthFailFipsModeTest.java new file mode 100644 index 00000000000..6bb09a14efe --- /dev/null +++ b/zookeeper-server/src/test/java/org/apache/zookeeper/test/SaslAuthFailFipsModeTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zookeeper.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.ZooDefs.Ids; +import org.apache.zookeeper.ZooKeeper; +import org.apache.zookeeper.common.X509Util; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +/** + * Tests that DIGEST-MD5 SASL authentication is rejected when FIPS mode is enabled. + * Does NOT extend SaslAuthDigestTestBase because that base class disables FIPS mode. + */ +public class SaslAuthFailFipsModeTest extends ClientBase { + + @BeforeAll + public static void setUpBeforeClass() { + System.setProperty(X509Util.FIPS_MODE_PROPERTY, "true"); + System.setProperty(SaslTestUtil.requireSASLAuthProperty, "true"); + System.setProperty(SaslTestUtil.authProviderProperty, SaslTestUtil.authProvider); + System.setProperty(SaslTestUtil.jaasConfig, SaslTestUtil.createJAASConfigFile("jaas_fips.conf", "test")); + } + + @AfterAll + public static void tearDownAfterClass() { + System.clearProperty(X509Util.FIPS_MODE_PROPERTY); + System.clearProperty(SaslTestUtil.requireSASLAuthProperty); + System.clearProperty(SaslTestUtil.authProviderProperty); + System.clearProperty(SaslTestUtil.jaasConfig); + } + + @Test + public void testDigestMd5RejectedInFipsMode() throws Exception { + ZooKeeper zk = null; + CountdownWatcher watcher = new CountdownWatcher(); + try { + zk = createClient(watcher); + zk.create("/fips-test", null, Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); + fail("DIGEST-MD5 SASL authentication should be rejected when FIPS mode is enabled."); + } catch (KeeperException e) { + assertEquals(KeeperException.Code.AUTHFAILED, e.code()); + watcher.waitForDisconnected(SaslTestUtil.CLIENT_DISCONNECT_TIMEOUT); + } finally { + if (zk != null) { + zk.close(); + } + } + } + +}