From 836540455a7d48c337593510de67303febf1131e Mon Sep 17 00:00:00 2001 From: uboness Date: Fri, 17 Oct 2014 14:33:22 -0700 Subject: [PATCH 1/3] Added additional Hasher implementations - `BCRYPT`, `MD5`, `SHA1`, `SHA2`, - Also removed the support for bcrypt minor version y (i.e. $2y$) as it's not supported by our BCrypt implementation Original commit: elastic/x-pack-elasticsearch@12cf024a59d02c5a6280021391a2f9653e552778 --- .../shield/authc/support/Hasher.java | 88 +++++++++++++++++-- .../esusers/FileUserPasswdStoreTests.java | 2 +- .../shield/authc/support/HasherTests.java | 34 +++++-- .../elasticsearch/shield/authc/esusers/users | 2 +- 4 files changed, 113 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/elasticsearch/shield/authc/support/Hasher.java b/src/main/java/org/elasticsearch/shield/authc/support/Hasher.java index 4090ad8cdc2..19b112cda18 100644 --- a/src/main/java/org/elasticsearch/shield/authc/support/Hasher.java +++ b/src/main/java/org/elasticsearch/shield/authc/support/Hasher.java @@ -9,6 +9,7 @@ import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.Crypt; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.codec.digest.Md5Crypt; +import org.apache.commons.codec.digest.Sha2Crypt; import org.elasticsearch.ElasticsearchIllegalArgumentException; import org.elasticsearch.common.os.OsUtils; @@ -34,9 +35,6 @@ public enum Hasher { @Override public boolean verify(SecuredString text, char[] hash) { String hashStr = new String(hash); - if (hashStr.startsWith(BCRYPT_PREFIX_Y)) { - hashStr = BCRYPT_PREFIX + hashStr.substring(BCRYPT_PREFIX_Y.length()); - } if (hashStr.startsWith(BCRYPT_PREFIX)) { return BCrypt.checkpw(text, hashStr); } @@ -52,16 +50,94 @@ public enum Hasher { String passwd64 = Base64.encodeBase64String(DigestUtils.sha1(textBytes)); return hashStr.substring(SHA1_PREFIX.length()).compareTo(passwd64) == 0; } + if (hashStr.startsWith(SHA2_PREFIX_5) || hashStr.startsWith(SHA2_PREFIX_6)) { + return hashStr.compareTo(Sha2Crypt.sha256Crypt(textBytes, hashStr)) == 0; + } return CRYPT_SUPPORTED ? - hashStr.compareTo(Crypt.crypt(textBytes, hashStr)) == 0 : // crypt algo - text.equals(hashStr); // plain text + hashStr.compareTo(Crypt.crypt(textBytes, hashStr)) == 0 : // crypt algo + text.equals(hashStr); // plain text + } + }, + + + BCRYPT() { + @Override + public char[] hash(SecuredString text) { + String salt = org.elasticsearch.shield.authc.support.BCrypt.gensalt(); + return BCrypt.hashpw(text, salt).toCharArray(); + } + + @Override + public boolean verify(SecuredString text, char[] hash) { + String hashStr = new String(hash); + if (!hashStr.startsWith(BCRYPT_PREFIX)) { + return false; + } + return BCrypt.checkpw(text, hashStr); + } + }, + + MD5() { + @Override + public char[] hash(SecuredString text) { + byte[] textBytes = CharArrays.toUtf8Bytes(text.internalChars()); + return Md5Crypt.apr1Crypt(textBytes).toCharArray(); + } + + @Override + public boolean verify(SecuredString text, char[] hash) { + String hashStr = new String(hash); + if (!hashStr.startsWith(APR1_PREFIX)) { + return false; + } + byte[] textBytes = CharArrays.toUtf8Bytes(text.internalChars()); + return hashStr.compareTo(Md5Crypt.apr1Crypt(textBytes, hashStr)) == 0; + } + }, + + SHA1() { + @Override + public char[] hash(SecuredString text) { + byte[] textBytes = CharArrays.toUtf8Bytes(text.internalChars()); + String hash = Base64.encodeBase64String(DigestUtils.sha1(textBytes)); + return (SHA1_PREFIX + hash).toCharArray(); + } + + @Override + public boolean verify(SecuredString text, char[] hash) { + String hashStr = new String(hash); + if (!hashStr.startsWith(SHA1_PREFIX)) { + return false; + } + byte[] textBytes = CharArrays.toUtf8Bytes(text.internalChars()); + String passwd64 = Base64.encodeBase64String(DigestUtils.sha1(textBytes)); + return hashStr.substring(SHA1_PREFIX.length()).compareTo(passwd64) == 0; + } + }, + + SHA2() { + @Override + public char[] hash(SecuredString text) { + byte[] textBytes = CharArrays.toUtf8Bytes(text.internalChars()); + return Sha2Crypt.sha256Crypt(textBytes).toCharArray(); + } + + @Override + public boolean verify(SecuredString text, char[] hash) { + String hashStr = new String(hash); + if (hashStr.startsWith(SHA2_PREFIX_5) || hashStr.startsWith(SHA2_PREFIX_6)) { + byte[] textBytes = CharArrays.toUtf8Bytes(text.internalChars()); + return hashStr.compareTo(Sha2Crypt.sha256Crypt(textBytes, hashStr)) == 0; + } + return false; } }; private static final String APR1_PREFIX = "$apr1$"; private static final String BCRYPT_PREFIX = "$2a$"; - private static final String BCRYPT_PREFIX_Y = "$2y$"; private static final String SHA1_PREFIX = "{SHA}"; + private static final String SHA2_PREFIX_5 = "$5$"; + private static final String SHA2_PREFIX_6 = "$6$"; private static final String PLAIN_PREFIX = "{plain}"; static final boolean CRYPT_SUPPORTED = !OsUtils.WINDOWS; diff --git a/src/test/java/org/elasticsearch/shield/authc/esusers/FileUserPasswdStoreTests.java b/src/test/java/org/elasticsearch/shield/authc/esusers/FileUserPasswdStoreTests.java index 1545c5c9f2d..00fdd6abe01 100644 --- a/src/test/java/org/elasticsearch/shield/authc/esusers/FileUserPasswdStoreTests.java +++ b/src/test/java/org/elasticsearch/shield/authc/esusers/FileUserPasswdStoreTests.java @@ -48,7 +48,7 @@ public class FileUserPasswdStoreTests extends ElasticsearchTestCase { assertThat(users, notNullValue()); assertThat(users.size(), is(6)); assertThat(users.get("bcrypt"), notNullValue()); - assertThat(new String(users.get("bcrypt")), equalTo("$2y$05$zxnP0vdREMxnEpkLCDI2OuSaSk/QEKA2.A42iOpI6U2u.RLLOWm1e")); + assertThat(new String(users.get("bcrypt")), equalTo("$2a$05$zxnP0vdREMxnEpkLCDI2OuSaSk/QEKA2.A42iOpI6U2u.RLLOWm1e")); assertThat(users.get("bcrypt10"), notNullValue()); assertThat(new String(users.get("bcrypt10")), equalTo("$2y$10$FMhmFjwU5.qxQ/BsEciS9OqcJVkFMgXMo4uH5CelOR1j4N9zIv67e")); assertThat(users.get("md5"), notNullValue()); diff --git a/src/test/java/org/elasticsearch/shield/authc/support/HasherTests.java b/src/test/java/org/elasticsearch/shield/authc/support/HasherTests.java index 7a3f626047d..4365a514031 100644 --- a/src/test/java/org/elasticsearch/shield/authc/support/HasherTests.java +++ b/src/test/java/org/elasticsearch/shield/authc/support/HasherTests.java @@ -5,7 +5,6 @@ */ package org.elasticsearch.shield.authc.support; -import org.elasticsearch.common.os.OsUtils; import org.elasticsearch.test.ElasticsearchTestCase; import org.junit.Test; @@ -15,11 +14,11 @@ import org.junit.Test; public class HasherTests extends ElasticsearchTestCase { @Test - public void testHtpasswdToolGenerated() throws Exception { + public void testHtpasswd_ToolGenerated() throws Exception { Hasher hasher = Hasher.HTPASSWD; SecuredString passwd = SecuredStringTests.build("test123"); assertTrue(hasher.verify(passwd, "$2a$05$zxnP0vdREMxnEpkLCDI2OuSaSk/QEKA2.A42iOpI6U2u.RLLOWm1e".toCharArray())); - assertTrue(hasher.verify(passwd, "$2a$10$FMhmFjwU5.qxQ/BsEciS9OqcJVkFMgXMo4uH5CelOR1j4N9zIv67e".toCharArray())); + assertTrue(hasher.verify(passwd, "$2a$10$vNMk6GyVUU./7YSZB6BGPuozm921GVPw/Pdukzd09s.sL2rIWROU6".toCharArray())); assertTrue(hasher.verify(passwd, "$apr1$R3DdqiAZ$aljIkaIVPSarmDMlJUBBP.".toCharArray())); if (!Hasher.CRYPT_SUPPORTED) { assertTrue(hasher.verify(passwd, "test123".toCharArray())); @@ -28,12 +27,37 @@ public class HasherTests extends ElasticsearchTestCase { } assertTrue(hasher.verify(passwd, "{plain}test123".toCharArray())); assertTrue(hasher.verify(passwd, "{SHA}cojt0Pw//L6ToM8G41aOKFIWh7w=".toCharArray())); + assertTrue(hasher.verify(passwd, "$5$RsqcsPiF$51tIIXf6oZb3Awox6FWNhITVlM/aW3oa8uN2eptIf54".toCharArray())); } @Test - public void testHtpasswdSelfGenerated() throws Exception { - Hasher hasher = Hasher.HTPASSWD; + public void testHtpasswd_SelfGenerated() throws Exception { + testHasherSelfGenerated(Hasher.HTPASSWD); + } + + @Test + public void testBcrypt_SelfGenerated() throws Exception { + testHasherSelfGenerated(Hasher.BCRYPT); + } + + @Test + public void testMd5_SelfGenerated() throws Exception { + testHasherSelfGenerated(Hasher.MD5); + } + + @Test + public void testSha1_SelfGenerated() throws Exception { + testHasherSelfGenerated(Hasher.SHA1); + } + + @Test + public void testSha2_SelfGenerated() throws Exception { + testHasherSelfGenerated(Hasher.SHA2); + } + + public void testHasherSelfGenerated(Hasher hasher) throws Exception { SecuredString passwd = SecuredStringTests.build("test123"); assertTrue(hasher.verify(passwd, hasher.hash(passwd))); } + } diff --git a/src/test/resources/org/elasticsearch/shield/authc/esusers/users b/src/test/resources/org/elasticsearch/shield/authc/esusers/users index 51469265909..df132ed1344 100644 --- a/src/test/resources/org/elasticsearch/shield/authc/esusers/users +++ b/src/test/resources/org/elasticsearch/shield/authc/esusers/users @@ -1,4 +1,4 @@ -bcrypt: $2y$05$zxnP0vdREMxnEpkLCDI2OuSaSk/QEKA2.A42iOpI6U2u.RLLOWm1e +bcrypt: $2a$05$zxnP0vdREMxnEpkLCDI2OuSaSk/QEKA2.A42iOpI6U2u.RLLOWm1e md5: $apr1$R3DdqiAZ$aljIkaIVPSarmDMlJUBBP. crypt: hsP1PYSLsEEvs plain: {plain}test123 From 91bbc5b2ea58e3079ed94843dbccbe39512c769d Mon Sep 17 00:00:00 2001 From: c-a-m Date: Fri, 17 Oct 2014 16:48:34 -0700 Subject: [PATCH 2/3] Fixes default ldap group to role mapping file Description: This fixes the name of the default file for group to role mapping. It was missing the extension Fixes https://github.com/elasticsearch/elasticsearch-shield/issues/223 Original commit: elastic/x-pack-elasticsearch@9ffcafd41e18107636472212e7fe49ee10613c20 --- .../elasticsearch/shield/authc/ldap/LdapGroupToRoleMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/elasticsearch/shield/authc/ldap/LdapGroupToRoleMapper.java b/src/main/java/org/elasticsearch/shield/authc/ldap/LdapGroupToRoleMapper.java index 0549b1d0d12..e74f8f052ba 100644 --- a/src/main/java/org/elasticsearch/shield/authc/ldap/LdapGroupToRoleMapper.java +++ b/src/main/java/org/elasticsearch/shield/authc/ldap/LdapGroupToRoleMapper.java @@ -33,7 +33,7 @@ import java.util.*; */ public class LdapGroupToRoleMapper extends AbstractComponent { - public static final String DEFAULT_FILE_NAME = "role_mapping"; + public static final String DEFAULT_FILE_NAME = "role_mapping.yml"; public static final String ROLE_MAPPING_FILE_SETTING = "files.role_mapping"; public static final String USE_UNMAPPED_GROUPS_AS_ROLES_SETTING = "unmapped_groups_as_roles"; From 229c9c6c7d7c4e541a75febb7456b127920df196 Mon Sep 17 00:00:00 2001 From: c-a-m Date: Fri, 17 Oct 2014 16:34:40 -0700 Subject: [PATCH 3/3] Truststore not needed when client auth is off With this change the truststore is loaded only if client-auth is turned on. This is causing problems because we never expect the http endpoint to have client auth, but it still requires us to have a truststore Fixes https://github.com/elasticsearch/elasticsearch-shield/issues/221 Original commit: elastic/x-pack-elasticsearch@40e2dc4de6acca549d0c1aeafe77e8845b6553d6 --- .../elasticsearch/shield/transport/ssl/SSLConfig.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/elasticsearch/shield/transport/ssl/SSLConfig.java b/src/main/java/org/elasticsearch/shield/transport/ssl/SSLConfig.java index 4ebdd8474f1..896781a5586 100644 --- a/src/main/java/org/elasticsearch/shield/transport/ssl/SSLConfig.java +++ b/src/main/java/org/elasticsearch/shield/transport/ssl/SSLConfig.java @@ -29,9 +29,12 @@ public class SSLConfig { private String[] ciphers; public SSLConfig(Settings componentSettings, Settings defaultSettings, boolean defaultRequireClientAuth) { - SSLTrustConfig sslTrustConfig = new SSLTrustConfig(componentSettings, defaultSettings); - this.clientAuth = componentSettings.getAsBoolean("require.client.auth", defaultSettings.getAsBoolean("require.client.auth", defaultRequireClientAuth)); + TrustManager[] trustManagers = null; + if (clientAuth) { + trustManagers = new SSLTrustConfig(componentSettings, defaultSettings).getTrustManagers(); + } + String keyStore = componentSettings.get("keystore", defaultSettings.get("keystore", System.getProperty("javax.net.ssl.keyStore"))); String keyStorePassword = componentSettings.get("keystore_password", defaultSettings.get("keystore_password", System.getProperty("javax.net.ssl.keyStorePassword"))); String keyStoreAlgorithm = componentSettings.get("keystore_algorithm", defaultSettings.get("keystore_algorithm", System.getProperty("ssl.KeyManagerFactory.algorithm"))); @@ -69,7 +72,7 @@ public class SSLConfig { try { String algorithm = componentSettings.get("context_algorithm", defaultSettings.get("shield.ssl.context_algorithm", "TLS")); sslContext = SSLContext.getInstance(algorithm); - sslContext.init(kmf.getKeyManagers(), sslTrustConfig.getTrustManagers(), null); + sslContext.init(kmf.getKeyManagers(), trustManagers, null); } catch (Exception e) { throw new ElasticsearchSSLException("Failed to initialize the SSLContext", e); }