You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by wi...@apache.org on 2015/03/04 11:06:05 UTC
[21/50] [abbrv] git commit: updated refs/heads/reporter to 178a938
CLOUDSTACK-5237: Add a default PBKDF2-SHA-256 based authenticator
Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
(cherry picked from commit 9533c54db669b22b268fcc21766e21c231e48d84)
Signed-off-by: Rohit Yadav <ro...@shapeblue.com>
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/6f4db0ce
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/6f4db0ce
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/6f4db0ce
Branch: refs/heads/reporter
Commit: 6f4db0ce4b493106eb4ac06737d3f2e9781ed535
Parents: d8e1bf1
Author: Rohit Yadav <ro...@shapeblue.com>
Authored: Fri Feb 27 15:45:06 2015 +0530
Committer: Rohit Yadav <ro...@shapeblue.com>
Committed: Fri Feb 27 15:53:58 2015 +0530
----------------------------------------------------------------------
client/pom.xml | 5 +
.../core/spring-core-registry-core-context.xml | 4 +-
plugins/pom.xml | 1 +
plugins/user-authenticators/pbkdf2/pom.xml | 29 ++++
.../cloudstack/pbkdf2/module.properties | 18 +++
.../cloudstack/pbkdf2/spring-pbkdf2-context.xml | 32 +++++
.../server/auth/PBKDF2UserAuthenticator.java | 143 +++++++++++++++++++
.../server/auth/PBKD2UserAuthenticatorTest.java | 75 ++++++++++
8 files changed, 305 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6f4db0ce/client/pom.xml
----------------------------------------------------------------------
diff --git a/client/pom.xml b/client/pom.xml
index 590a9ad..9453159 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -77,6 +77,11 @@
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloud-plugin-user-authenticator-pbkdf2</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-user-authenticator-plaintext</artifactId>
<version>${project.version}</version>
</dependency>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6f4db0ce/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
----------------------------------------------------------------------
diff --git a/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml b/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
index 939cffe..d967540 100644
--- a/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
+++ b/core/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml
@@ -33,7 +33,7 @@
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
<property name="orderConfigKey" value="user.authenticators.order" />
<property name="excludeKey" value="user.authenticators.exclude" />
- <property name="orderConfigDefault" value="SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT" />
+ <property name="orderConfigDefault" value="PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT" />
</bean>
<bean id="pluggableAPIAuthenticatorsRegistry"
@@ -47,7 +47,7 @@
class="org.apache.cloudstack.spring.lifecycle.registry.ExtensionRegistry">
<property name="orderConfigKey" value="user.password.encoders.order" />
<property name="excludeKey" value="user.password.encoders.exclude" />
- <property name="orderConfigDefault" value="SHA256SALT,MD5,LDAP,PLAINTEXT" />
+ <property name="orderConfigDefault" value="PBKDF2,SHA256SALT,MD5,LDAP,SAML2,PLAINTEXT" />
</bean>
<bean id="securityCheckersRegistry"
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6f4db0ce/plugins/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/pom.xml b/plugins/pom.xml
index 76119dc..962ce46 100755
--- a/plugins/pom.xml
+++ b/plugins/pom.xml
@@ -71,6 +71,7 @@
<module>storage-allocators/random</module>
<module>user-authenticators/ldap</module>
<module>user-authenticators/md5</module>
+ <module>user-authenticators/pbkdf2</module>
<module>user-authenticators/plain-text</module>
<module>user-authenticators/saml2</module>
<module>user-authenticators/sha256salted</module>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6f4db0ce/plugins/user-authenticators/pbkdf2/pom.xml
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/pbkdf2/pom.xml b/plugins/user-authenticators/pbkdf2/pom.xml
new file mode 100644
index 0000000..e656045
--- /dev/null
+++ b/plugins/user-authenticators/pbkdf2/pom.xml
@@ -0,0 +1,29 @@
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>cloud-plugin-user-authenticator-pbkdf2</artifactId>
+ <name>Apache CloudStack Plugin - User Authenticator PBKDF2-SHA-256</name>
+ <parent>
+ <groupId>org.apache.cloudstack</groupId>
+ <artifactId>cloudstack-plugins</artifactId>
+ <version>4.5.0-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+</project>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6f4db0ce/plugins/user-authenticators/pbkdf2/resources/META-INF/cloudstack/pbkdf2/module.properties
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/pbkdf2/resources/META-INF/cloudstack/pbkdf2/module.properties b/plugins/user-authenticators/pbkdf2/resources/META-INF/cloudstack/pbkdf2/module.properties
new file mode 100644
index 0000000..7c2b38d
--- /dev/null
+++ b/plugins/user-authenticators/pbkdf2/resources/META-INF/cloudstack/pbkdf2/module.properties
@@ -0,0 +1,18 @@
+# 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.
+name=pbkdf2
+parent=api
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6f4db0ce/plugins/user-authenticators/pbkdf2/resources/META-INF/cloudstack/pbkdf2/spring-pbkdf2-context.xml
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/pbkdf2/resources/META-INF/cloudstack/pbkdf2/spring-pbkdf2-context.xml b/plugins/user-authenticators/pbkdf2/resources/META-INF/cloudstack/pbkdf2/spring-pbkdf2-context.xml
new file mode 100644
index 0000000..a6272dd
--- /dev/null
+++ b/plugins/user-authenticators/pbkdf2/resources/META-INF/cloudstack/pbkdf2/spring-pbkdf2-context.xml
@@ -0,0 +1,32 @@
+<!--
+ 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.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:aop="http://www.springframework.org/schema/aop"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
+ http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context-3.0.xsd"
+ >
+ <bean id="PBKDF2UserAuthenticator" class="org.apache.cloudstack.server.auth.PBKDF2UserAuthenticator">
+ <property name="name" value="PBKDF2"/>
+ </bean>
+</beans>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6f4db0ce/plugins/user-authenticators/pbkdf2/src/org/apache/cloudstack/server/auth/PBKDF2UserAuthenticator.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/pbkdf2/src/org/apache/cloudstack/server/auth/PBKDF2UserAuthenticator.java b/plugins/user-authenticators/pbkdf2/src/org/apache/cloudstack/server/auth/PBKDF2UserAuthenticator.java
new file mode 100644
index 0000000..43c32c7
--- /dev/null
+++ b/plugins/user-authenticators/pbkdf2/src/org/apache/cloudstack/server/auth/PBKDF2UserAuthenticator.java
@@ -0,0 +1,143 @@
+// 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.cloudstack.server.auth;
+
+import com.cloud.server.auth.DefaultUserAuthenticator;
+import com.cloud.server.auth.UserAuthenticator;
+import com.cloud.user.UserAccount;
+import com.cloud.user.dao.UserAccountDao;
+import com.cloud.utils.ConstantTimeComparator;
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.bouncycastle.crypto.PBEParametersGenerator;
+import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.encoders.Base64;
+
+import javax.ejb.Local;
+import javax.inject.Inject;
+import java.io.UnsupportedEncodingException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Map;
+
+import static java.lang.String.format;
+
+@Local({UserAuthenticator.class})
+public class PBKDF2UserAuthenticator extends DefaultUserAuthenticator {
+ public static final Logger s_logger = Logger.getLogger(PBKDF2UserAuthenticator.class);
+ private static final int s_saltlen = 64;
+ private static final int s_rounds = 100000;
+ private static final int s_keylen = 512;
+
+ @Inject
+ private UserAccountDao _userAccountDao;
+
+ public Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication> authenticate(String username, String password, Long domainId, Map<String, Object[]> requestParameters) {
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Retrieving user: " + username);
+ }
+ boolean isValidUser = false;
+ UserAccount user = this._userAccountDao.getUserAccount(username, domainId);
+ if (user != null) {
+ isValidUser = true;
+ } else {
+ s_logger.debug("Unable to find user with " + username + " in domain " + domainId);
+ }
+
+ byte[] salt = new byte[0];
+ int rounds = s_rounds;
+ try {
+ if (isValidUser) {
+ String[] storedPassword = user.getPassword().split(":");
+ if ((storedPassword.length != 3) || (!StringUtils.isNumeric(storedPassword[2]))) {
+ s_logger.warn("The stored password for " + username + " isn't in the right format for this authenticator");
+ isValidUser = false;
+ } else {
+ // Encoding format = <salt>:<password hash>:<rounds>
+ salt = decode(storedPassword[0]);
+ rounds = Integer.parseInt(storedPassword[2]);
+ }
+ }
+ boolean result = false;
+ if (isValidUser && validateCredentials(password, salt)) {
+ result = ConstantTimeComparator.compareStrings(user.getPassword(), encode(password, salt, rounds));
+ }
+
+ UserAuthenticator.ActionOnFailedAuthentication action = null;
+ if ((!result) && (isValidUser)) {
+ action = UserAuthenticator.ActionOnFailedAuthentication.INCREMENT_INCORRECT_LOGIN_ATTEMPT_COUNT;
+ }
+ return new Pair(Boolean.valueOf(result), action);
+ } catch (NumberFormatException e) {
+ throw new CloudRuntimeException("Unable to hash password", e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new CloudRuntimeException("Unable to hash password", e);
+ } catch (UnsupportedEncodingException e) {
+ throw new CloudRuntimeException("Unable to hash password", e);
+ } catch (InvalidKeySpecException e) {
+ throw new CloudRuntimeException("Unable to hash password", e);
+ }
+ }
+
+ public String encode(String password)
+ {
+ try
+ {
+ return encode(password, makeSalt(), s_rounds);
+ } catch (NoSuchAlgorithmException e) {
+ throw new CloudRuntimeException("Unable to hash password", e);
+ } catch (UnsupportedEncodingException e) {
+ throw new CloudRuntimeException("Unable to hash password", e);
+ } catch (InvalidKeySpecException e) {
+ s_logger.error("Exception in EncryptUtil.createKey ", e);
+ throw new CloudRuntimeException("Unable to hash password", e);
+ }
+ }
+
+ public String encode(String password, byte[] salt, int rounds)
+ throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeySpecException {
+ PKCS5S2ParametersGenerator generator = new PKCS5S2ParametersGenerator();
+ generator.init(PBEParametersGenerator.PKCS5PasswordToBytes(
+ password.toCharArray()),
+ salt,
+ rounds);
+ return format("%s:%s:%d", encode(salt),
+ encode(((KeyParameter)generator.generateDerivedParameters(s_keylen)).getKey()), rounds);
+ }
+
+ public static byte[] makeSalt() throws NoSuchAlgorithmException {
+ SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
+ byte[] salt = new byte[s_saltlen];
+ sr.nextBytes(salt);
+ return salt;
+ }
+
+ private static boolean validateCredentials(String plainPassword, byte[] hash) {
+ return !(plainPassword == null || plainPassword.isEmpty() || hash == null || hash.length == 0);
+ }
+
+ private static String encode(byte[] input) {
+ return new String(Base64.encode(input));
+ }
+
+ private static byte[] decode(String input) throws UnsupportedEncodingException {
+ return Base64.decode(input.getBytes("UTF-8"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6f4db0ce/plugins/user-authenticators/pbkdf2/test/org/apache/cloudstack/server/auth/PBKD2UserAuthenticatorTest.java
----------------------------------------------------------------------
diff --git a/plugins/user-authenticators/pbkdf2/test/org/apache/cloudstack/server/auth/PBKD2UserAuthenticatorTest.java b/plugins/user-authenticators/pbkdf2/test/org/apache/cloudstack/server/auth/PBKD2UserAuthenticatorTest.java
new file mode 100644
index 0000000..f401416
--- /dev/null
+++ b/plugins/user-authenticators/pbkdf2/test/org/apache/cloudstack/server/auth/PBKD2UserAuthenticatorTest.java
@@ -0,0 +1,75 @@
+// 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.cloudstack.server.auth;
+
+import com.cloud.server.auth.UserAuthenticator;
+import com.cloud.user.UserAccountVO;
+import com.cloud.user.dao.UserAccountDao;
+import com.cloud.utils.Pair;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import java.lang.reflect.Field;
+import java.security.NoSuchAlgorithmException;
+
+@RunWith(MockitoJUnitRunner.class)
+public class PBKD2UserAuthenticatorTest {
+ @Mock
+ UserAccountDao dao;
+
+ @Test
+ public void encodePasswordTest() {
+ PBKDF2UserAuthenticator authenticator = new PBKDF2UserAuthenticator();
+ String encodedPassword = authenticator.encode("password123ABCS!@#$%");
+ Assert.assertTrue(encodedPassword.length() < 255 && encodedPassword.length() >= 182);
+ }
+
+ @Test
+ public void saltTest() throws NoSuchAlgorithmException {
+ byte[] salt = new PBKDF2UserAuthenticator().makeSalt();
+ Assert.assertTrue(salt.length > 16);
+ }
+
+ @Test
+ public void authenticateValidTest() throws IllegalAccessException, NoSuchFieldException {
+ PBKDF2UserAuthenticator authenticator = new PBKDF2UserAuthenticator();
+ Field daoField = PBKDF2UserAuthenticator.class.getDeclaredField("_userAccountDao");
+ daoField.setAccessible(true);
+ daoField.set(authenticator, dao);
+ UserAccountVO account = new UserAccountVO();
+ account.setPassword("FMDMdx/2QjrZniqNRAgOAC1ai/CY/C+2kmKhp3vo+98pkqhO+AR6hCyUl0bOXtkq3XWqNiSQTwbi7KTiwuWhyw==:+u8T5LzCtikCPvKnUDn6JDezf1Hg2bood/ke5Oo93pz9s1eD9k/JLsa497Z3h9QWfOQfq0zvCRmkzfXMF913vQ==:4096");
+ Mockito.when(dao.getUserAccount(Mockito.anyString(), Mockito.anyLong())).thenReturn(account);
+ Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication> pair = authenticator.authenticate("admin", "password", 1l, null);
+ Assert.assertTrue(pair.first());
+ }
+
+ @Test
+ public void authenticateInValidTest() throws IllegalAccessException, NoSuchFieldException {
+ PBKDF2UserAuthenticator authenticator = new PBKDF2UserAuthenticator();
+ Field daoField = PBKDF2UserAuthenticator.class.getDeclaredField("_userAccountDao");
+ daoField.setAccessible(true);
+ daoField.set(authenticator, dao);
+ UserAccountVO account = new UserAccountVO();
+ account.setPassword("5f4dcc3b5aa765d61d8327deb882cf99");
+ Mockito.when(dao.getUserAccount(Mockito.anyString(), Mockito.anyLong())).thenReturn(account);
+ Pair<Boolean, UserAuthenticator.ActionOnFailedAuthentication> pair = authenticator.authenticate("admin", "password", 1l, null);
+ Assert.assertFalse(pair.first());
+ }
+}