This is an automated email from the ASF dual-hosted git repository.
anmolnar pushed a commit to branch branch-3.9
in repository https://gitbox.apache.org/repos/asf/zookeeper.git
The following commit(s) were added to refs/heads/branch-3.9 by this push:
new 17e78b1da ZOOKEEPER-5043: Disable fallback to DIGEST-MD5 in SaslServer
when FIPS mode is set
17e78b1da is described below
commit 17e78b1da0e524e1ce044e902ef178b35f4ceaf2
Author: Dávid Paksy <[email protected]>
AuthorDate: Tue May 12 17:29:11 2026 +0200
ZOOKEEPER-5043: Disable fallback to DIGEST-MD5 in SaslServer when FIPS mode
is set
Reviewers: anmolnar
Author: PDavid
Closes #2384 from PDavid/ZOOKEEPER-5043-disable-DIGEST-MD5-branch-3.9
---
.../zookeeper-client-c/tests/zkServer.sh | 3 +-
.../zookeeper/server/ZooKeeperSaslServer.java | 11 +++-
.../server/quorum/auth/SaslQuorumAuthServer.java | 8 ++-
.../org/apache/zookeeper/util/SecurityUtils.java | 7 +++
.../zookeeper/test/SaslAuthFailFipsModeTest.java | 72 ++++++++++++++++++++++
5 files changed, 97 insertions(+), 4 deletions(-)
diff --git a/zookeeper-client/zookeeper-client-c/tests/zkServer.sh
b/zookeeper-client/zookeeper-client-c/tests/zkServer.sh
index 7505bf3a8..c7dd9bb10 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 bf5089d79..98c8d0e6b 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 a1425833b..ca651115d 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<String> authzHosts) throws SaslException {
this.quorumRequireSasl = quorumRequireSasl;
@@ -60,7 +61,8 @@ public SaslQuorumAuthServer(boolean quorumRequireSasl, String
loginContext, Set<
Supplier<CallbackHandler> 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 5c44f2116..e29dabcf0 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 000000000..6bb09a14e
--- /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();
+ }
+ }
+ }
+
+}