Bläddra i källkod

Validate hashing algorithm in users tool (#55628)

This change adds validation when running the users tool so that
if Elasticsearch is expected to run in a JVM that is configured to
be in FIPS 140 mode and the password hashing algorithm is not
compliant, we would throw an error.
Users tool uses the configuration from the node and this validation
would also happen upon node startup but users might be added in the
file realm before the node is started and we would have the
opportunity to notify the user of this misconfiguration.
The changes in #55544 make this much less probable to happen in 8
since the default algorithm will be compliant but this change can
act as a fallback in anycase and makes for a better user experience.
Ioannis Kakavas 5 år sedan
förälder
incheckning
4afb360cb3

+ 4 - 0
x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/tool/UsersTool.java

@@ -439,6 +439,10 @@ public class UsersTool extends LoggingAwareMultiCommand {
 
     private static char[] getPasswordHash(Terminal terminal, Environment env, String cliPasswordValue) throws UserException {
         final Hasher hasher = Hasher.resolve(XPackSettings.PASSWORD_HASHING_ALGORITHM.get(env.settings()));
+        if (XPackSettings.FIPS_MODE_ENABLED.get(env.settings()) && hasher.name().toLowerCase(Locale.ROOT).startsWith("pbkdf2") == false) {
+            throw new UserException(ExitCodes.CONFIG, "Only PBKDF2 is allowed for password hashing in a FIPS 140 JVM. Please set the " +
+                "appropriate value for [ " + XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey() + " ] setting.");
+        }
         final char[] passwordHash;
         try (SecureString password = parsePassword(terminal, cliPasswordValue)) {
             passwordHash = hasher.hash(password);

+ 40 - 8
x-pack/qa/security-tools-tests/src/test/java/org/elasticsearch/xpack/security/authc/file/tool/UsersToolTests.java

@@ -70,7 +70,8 @@ public class UsersToolTests extends CommandTestCase {
         IOUtils.rm(homeDir);
         confDir = homeDir.resolve("config");
         Files.createDirectories(confDir);
-        hasher = Hasher.resolve(randomFrom("bcrypt", "pbkdf2"));
+        hasher = inFipsJvm() ? randomFrom(Hasher.PBKDF2, Hasher.PBKDF2_1000)
+            : randomFrom(Hasher.PBKDF2_1000, Hasher.PBKDF2, Hasher.BCRYPT, Hasher.BCRYPT9);
         String defaultPassword = SecuritySettingsSourceField.TEST_PASSWORD;
         Files.write(confDir.resolve("users"), Arrays.asList(
             "existing_user:" + new String(hasher.hash(SecuritySettingsSourceField.TEST_PASSWORD_SECURE_STRING)),
@@ -90,10 +91,11 @@ public class UsersToolTests extends CommandTestCase {
             "  cluster: all"
         ), StandardCharsets.UTF_8);
         settings =
-                Settings.builder()
-                        .put("path.home", homeDir)
-                        .put("xpack.security.authc.realms.file.file.order", 0)
-                        .build();
+            Settings.builder()
+                .put("path.home", homeDir)
+                .put("xpack.security.authc.realms.file.file.order", 0)
+                .put("xpack.security.authc.password_hashing.algorithm", hasher.name())
+                .build();
         pathHomeParameter = "-Epath.home=" + homeDir;
         fileOrderParameter = "-Expack.security.authc.realms.file.file.order=0";
     }
@@ -174,9 +176,7 @@ public class UsersToolTests extends CommandTestCase {
             }
             String gotHash = usernameHash[1];
             SecureString expectedHash = new SecureString(password.toCharArray());
-            // CommandTestCase#execute runs passwd with default settings, so bcrypt with cost of 10
-            Hasher bcryptHasher = Hasher.resolve("bcrypt");
-            assertTrue("Could not validate password for user", bcryptHasher.verify(expectedHash, gotHash.toCharArray()));
+            assertTrue("Could not validate password for user", hasher.verify(expectedHash, gotHash.toCharArray()));
             return;
         }
         fail("Could not find username " + username + " in users file:\n" + lines.toString());
@@ -363,6 +363,23 @@ public class UsersToolTests extends CommandTestCase {
         assertTrue(lines.toString(), lines.isEmpty());
     }
 
+    public void testAddUserWithInvalidHashingAlgorithmInFips() throws Exception {
+        settings =
+            Settings.builder()
+                .put(settings)
+                .put("xpack.security.authc.password_hashing.algorithm", "bcrypt")
+                .put("xpack.security.fips_mode.enabled", true)
+                .build();
+
+        UserException e = expectThrows(UserException.class, () -> {
+            execute("useradd", pathHomeParameter, fileOrderParameter, randomAlphaOfLength(12), "-p",
+                SecuritySettingsSourceField.TEST_PASSWORD);
+        });
+        assertEquals(ExitCodes.CONFIG, e.exitCode);
+        assertEquals("Only PBKDF2 is allowed for password hashing in a FIPS 140 JVM. " +
+            "Please set the appropriate value for [ xpack.security.authc.password_hashing.algorithm ] setting.", e.getMessage());
+    }
+
     public void testUserdelUnknownUser() throws Exception {
         UserException e = expectThrows(UserException.class, () -> {
             execute("userdel", pathHomeParameter, fileOrderParameter, "unknown");
@@ -398,6 +415,21 @@ public class UsersToolTests extends CommandTestCase {
         assertRole("test_admin", "existing_user"); // roles unchanged
     }
 
+    public void testPasswdWithInvalidHashingAlgorithmInFips() throws Exception {
+        settings =
+            Settings.builder()
+                .put(settings)
+                .put("xpack.security.authc.password_hashing.algorithm", "bcrypt")
+                .put("xpack.security.fips_mode.enabled", true)
+                .build();
+        UserException e = expectThrows(UserException.class, () -> {
+            execute("passwd", pathHomeParameter, fileOrderParameter, "existing_user", "-p", "newpassword");
+        });
+        assertEquals(ExitCodes.CONFIG, e.exitCode);
+        assertEquals("Only PBKDF2 is allowed for password hashing in a FIPS 140 JVM. " +
+            "Please set the appropriate value for [ xpack.security.authc.password_hashing.algorithm ] setting.", e.getMessage());
+    }
+
     public void testRolesUnknownUser() throws Exception {
         UserException e = expectThrows(UserException.class, () -> {
             execute("roles", pathHomeParameter, fileOrderParameter, "unknown");