CLOUDSTACK-1734: Make SHA1 default password encoding mechanism

Description:

	Making SHA256SALT the default encoding algorithm to encode
	passwords when creating/updating users.

	Introducing a new configurable list to allow admins to
	separately configure the order of preference for encoding
	and authentication schemes.

	Since passwords are now sent by clients as clear text,
	fixing the Plain text authenticator to check against the
	password passed in rather than its md5 digest.
This commit is contained in:
Vijayendra Bhamidipati 2013-04-02 11:07:41 -07:00 committed by Min Chen
parent 58c962ef15
commit 2dbdc46337
13 changed files with 99 additions and 93 deletions

View File

@ -63,7 +63,7 @@ public class CreateAccountCmd extends BaseCmd {
@Parameter(name=ApiConstants.LASTNAME, type=CommandType.STRING, required=true, description="lastname") @Parameter(name=ApiConstants.LASTNAME, type=CommandType.STRING, required=true, description="lastname")
private String lastName; private String lastName;
@Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required=true, description="Hashed password (Default is MD5). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter See Docs section.") @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required=true, description="Clear text password (Default hashed to SHA256SALT). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter See Docs section.")
private String password; private String password;
@Parameter(name=ApiConstants.TIMEZONE, type=CommandType.STRING, description="Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") @Parameter(name=ApiConstants.TIMEZONE, type=CommandType.STRING, description="Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.")

View File

@ -56,7 +56,7 @@ public class CreateUserCmd extends BaseCmd {
@Parameter(name=ApiConstants.LASTNAME, type=CommandType.STRING, required=true, description="lastname") @Parameter(name=ApiConstants.LASTNAME, type=CommandType.STRING, required=true, description="lastname")
private String lastname; private String lastname;
@Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required=true, description="Hashed password (Default is MD5). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter See Docs section.") @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, required=true, description="Clear text password (Default hashed to SHA256SALT). If you wish to use any other hashing algorithm, you would need to write a custom authentication adapter See Docs section.")
private String password; private String password;
@Parameter(name=ApiConstants.TIMEZONE, type=CommandType.STRING, description="Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.") @Parameter(name=ApiConstants.TIMEZONE, type=CommandType.STRING, description="Specifies a timezone for this command. For more information on the timezone parameter, see Time Zone Format.")

View File

@ -59,7 +59,7 @@ public class UpdateUserCmd extends BaseCmd {
@Parameter(name=ApiConstants.LASTNAME, type=CommandType.STRING, description="last name") @Parameter(name=ApiConstants.LASTNAME, type=CommandType.STRING, description="last name")
private String lastname; private String lastname;
@Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="Hashed password (default is MD5). If you wish to use any other hasing algorithm, you would need to write a custom authentication adapter") @Parameter(name=ApiConstants.PASSWORD, type=CommandType.STRING, description="Clear text password (default hashed to SHA256SALT). If you wish to use any other hasing algorithm, you would need to write a custom authentication adapter")
private String password; private String password;
@Parameter(name=ApiConstants.SECRET_KEY, type=CommandType.STRING, description="The secret key for the user. Must be specified with userApiKey") @Parameter(name=ApiConstants.SECRET_KEY, type=CommandType.STRING, description="The secret key for the user. Must be specified with userApiKey")

View File

@ -379,6 +379,60 @@
<bean id="LDAPUserAuthenticator" class="com.cloud.server.auth.LDAPUserAuthenticator"> <bean id="LDAPUserAuthenticator" class="com.cloud.server.auth.LDAPUserAuthenticator">
<property name="name" value="LDAP"/> <property name="name" value="LDAP"/>
</bean> </bean>
<bean id="SHA256SaltedUserAuthenticator" class="com.cloud.server.auth.SHA256SaltedUserAuthenticator">
<property name="name" value="SHA256SALT"/>
</bean>
<bean id="PlainTextUserAuthenticator" class="com.cloud.server.auth.PlainTextUserAuthenticator">
<property name="name" value="PLAINTEXT"/>
</bean>
<bean id="accountManagerImpl" class="com.cloud.user.AccountManagerImpl" >
<property name="UserAuthenticators">
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>
<property name="UserPasswordEncoders">
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>
<property name="SecurityCheckers">
<list>
<ref bean="domainChecker"/>
</list>
</property>
</bean>
<bean id="managementServerImpl" class ="com.cloud.server.ManagementServerImpl">
<property name="UserAuthenticators">
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>
<property name="UserPasswordEncoders">
<list>
<ref bean="SHA256SaltedUserAuthenticator"/>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
<ref bean="PlainTextUserAuthenticator"/>
</list>
</property>
<property name="HostAllocators">
<list>
<ref bean="FirstFitRouting"/>
</list>
</property>
</bean>
<!-- <!--
Network Elements Network Elements

View File

@ -40,34 +40,6 @@
<!-- <!--
Managers & pluggable adapters configuration under OSS deployment Managers & pluggable adapters configuration under OSS deployment
--> -->
<bean id="accountManagerImpl" class="com.cloud.user.AccountManagerImpl" >
<property name="UserAuthenticators">
<list>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
</list>
</property>
<property name="SecurityCheckers">
<list>
<ref bean="domainChecker"/>
</list>
</property>
</bean>
<bean id="managementServerImpl" class ="com.cloud.server.ManagementServerImpl">
<property name="UserAuthenticators">
<list>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
</list>
</property>
<property name="HostAllocators">
<list>
<ref bean="FirstFitRouting"/>
</list>
</property>
</bean>
<bean id="storageManagerImpl" class="com.cloud.storage.StorageManagerImpl"> <bean id="storageManagerImpl" class="com.cloud.storage.StorageManagerImpl">
<property name="StoragePoolAllocators"> <property name="StoragePoolAllocators">
<list> <list>

View File

@ -131,34 +131,6 @@
<!-- <!--
Managers & pluggable adapters configuration under non-OSS deployment Managers & pluggable adapters configuration under non-OSS deployment
--> -->
<bean id="accountManagerImpl" class="com.cloud.user.AccountManagerImpl" >
<property name="UserAuthenticators">
<list>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
</list>
</property>
<property name="SecurityCheckers">
<list>
<ref bean="domainChecker"/>
</list>
</property>
</bean>
<bean id="managementServerImpl" class ="com.cloud.server.ManagementServerImpl">
<property name="UserAuthenticators">
<list>
<ref bean="MD5UserAuthenticator"/>
<ref bean="LDAPUserAuthenticator"/>
</list>
</property>
<property name="HostAllocators">
<list>
<ref bean="FirstFitRouting"/>
</list>
</property>
</bean>
<bean id="storageManagerImpl" class="com.cloud.storage.StorageManagerImpl"> <bean id="storageManagerImpl" class="com.cloud.storage.StorageManagerImpl">
<property name="StoragePoolAllocators"> <property name="StoragePoolAllocators">
<list> <list>

View File

@ -36,7 +36,7 @@ INSERT INTO `cloud`.`user` (id, uuid, username, password, account_id, firstname,
-- Add system user with encrypted password=password -- Add system user with encrypted password=password
INSERT INTO `cloud`.`user` (id, uuid, username, password, account_id, firstname, INSERT INTO `cloud`.`user` (id, uuid, username, password, account_id, firstname,
lastname, email, state, created) VALUES (2, UUID(), 'admin', '5f4dcc3b5aa765d61d8327deb882cf99', lastname, email, state, created) VALUES (2, UUID(), 'admin', '5f4dcc3b5aa765d61d8327deb882cf99',
'2', 'Admin', 'User', 'admin@mailprovider.com', 'enabled', NOW()); '2', 'Admin', 'User', 'admin@mailprovider.com', 'disabled', NOW());
-- Add configurations -- Add configurations
INSERT INTO `cloud`.`configuration` (category, instance, component, name, value) INSERT INTO `cloud`.`configuration` (category, instance, component, name, value)

View File

@ -151,7 +151,10 @@ public class LDAPUserAuthenticator extends DefaultUserAuthenticator {
@Override @Override
public boolean configure(String name, Map<String, Object> params) public boolean configure(String name, Map<String, Object> params)
throws ConfigurationException { throws ConfigurationException {
super.configure(name, params); if (name == null) {
name = "LDAP";
}
super.configure(name, params);
return true; return true;
} }

View File

@ -59,8 +59,12 @@ public class MD5UserAuthenticator extends DefaultUserAuthenticator {
return true; return true;
} }
@Override
public boolean configure(String name, Map<String, Object> params) public boolean configure(String name, Map<String, Object> params)
throws ConfigurationException { throws ConfigurationException {
if(name == null) {
name = "MD5";
}
super.configure(name, params); super.configure(name, params);
return true; return true;
} }

View File

@ -28,7 +28,6 @@ import org.apache.log4j.Logger;
import com.cloud.user.UserAccount; import com.cloud.user.UserAccount;
import com.cloud.user.dao.UserAccountDao; import com.cloud.user.dao.UserAccountDao;
import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.exception.CloudRuntimeException;
@ -43,45 +42,26 @@ public class PlainTextUserAuthenticator extends DefaultUserAuthenticator {
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
s_logger.debug("Retrieving user: " + username); s_logger.debug("Retrieving user: " + username);
} }
UserAccount user = _userAccountDao.getUserAccount(username, domainId); UserAccount user = _userAccountDao.getUserAccount(username, domainId);
if (user == null) { if (user == null) {
s_logger.debug("Unable to find user with " + username + " in domain " + domainId); s_logger.debug("Unable to find user with " + username + " in domain " + domainId);
return false; return false;
} }
if (!user.getPassword().equals(password)) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new CloudRuntimeException("Error", e);
}
md5.reset();
BigInteger pwInt = new BigInteger(1, md5.digest(password.getBytes()));
// make sure our MD5 hash value is 32 digits long...
StringBuffer sb = new StringBuffer();
String pwStr = pwInt.toString(16);
int padding = 32 - pwStr.length();
for (int i = 0; i < padding; i++) {
sb.append('0');
}
sb.append(pwStr);
// Will: The MD5Authenticator is now a straight pass-through comparison of the
// the passwords because we will not assume that the password passed in has
// already been MD5 hashed. I am keeping the above code in case this requirement changes
// or people need examples of how to MD5 hash passwords in java.
if (!user.getPassword().equals(sb.toString())) {
s_logger.debug("Password does not match"); s_logger.debug("Password does not match");
return false; return false;
} }
return true; return true;
} }
@Override
public boolean configure(String name, Map<String, Object> params) public boolean configure(String name, Map<String, Object> params)
throws ConfigurationException { throws ConfigurationException {
if (name == null) {
name = "PLAINTEXT";
}
super.configure(name, params); super.configure(name, params);
return true; return true;
} }

View File

@ -44,6 +44,9 @@ public class SHA256SaltedUserAuthenticator extends DefaultUserAuthenticator {
@Override @Override
public boolean configure(String name, Map<String, Object> params) public boolean configure(String name, Map<String, Object> params)
throws ConfigurationException { throws ConfigurationException {
if (name == null) {
name = "SHA256SALT";
}
super.configure(name, params); super.configure(name, params);
return true; return true;
} }

View File

@ -457,7 +457,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
private Map<String, Boolean> _availableIdsMap; private Map<String, Boolean> _availableIdsMap;
List<UserAuthenticator> _userAuthenticators; private List<UserAuthenticator> _userAuthenticators;
private List<UserAuthenticator> _userPasswordEncoders;
@Inject ClusterManager _clusterMgr; @Inject ClusterManager _clusterMgr;
private String _hashKey = null; private String _hashKey = null;
@ -473,7 +474,15 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
public void setUserAuthenticators(List<UserAuthenticator> authenticators) { public void setUserAuthenticators(List<UserAuthenticator> authenticators) {
_userAuthenticators = authenticators; _userAuthenticators = authenticators;
} }
public List<UserAuthenticator> getUserPasswordEncoders() {
return _userPasswordEncoders;
}
public void setUserPasswordEncoders(List<UserAuthenticator> encoders) {
_userPasswordEncoders = encoders;
}
public List<HostAllocator> getHostAllocators() { public List<HostAllocator> getHostAllocators() {
return _hostAllocators; return _hostAllocators;
} }
@ -3342,7 +3351,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
// This means its a new account, set the password using the // This means its a new account, set the password using the
// authenticator // authenticator
for (UserAuthenticator authenticator: _userAuthenticators) { for (UserAuthenticator authenticator: _userPasswordEncoders) {
encodedPassword = authenticator.encode(password); encodedPassword = authenticator.encode(password);
if (encodedPassword != null) { if (encodedPassword != null) {
break; break;

View File

@ -222,6 +222,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
@Inject VolumeManager volumeMgr; @Inject VolumeManager volumeMgr;
private List<UserAuthenticator> _userAuthenticators; private List<UserAuthenticator> _userAuthenticators;
List<UserAuthenticator> _userPasswordEncoders;
private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AccountChecker")); private final ScheduledExecutorService _executor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AccountChecker"));
@ -241,7 +242,15 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
public void setUserAuthenticators(List<UserAuthenticator> authenticators) { public void setUserAuthenticators(List<UserAuthenticator> authenticators) {
_userAuthenticators = authenticators; _userAuthenticators = authenticators;
} }
public List<UserAuthenticator> getUserPasswordEncoders() {
return _userPasswordEncoders;
}
public void setUserPasswordEncoders(List<UserAuthenticator> encoders) {
_userPasswordEncoders = encoders;
}
public List<SecurityChecker> getSecurityCheckers() { public List<SecurityChecker> getSecurityCheckers() {
return _securityCheckers; return _securityCheckers;
} }
@ -947,7 +956,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
if (password != null) { if (password != null) {
String encodedPassword = null; String encodedPassword = null;
for (Iterator<UserAuthenticator> en = _userAuthenticators.iterator(); en.hasNext();) { for (Iterator<UserAuthenticator> en = _userPasswordEncoders.iterator(); en.hasNext();) {
UserAuthenticator authenticator = en.next(); UserAuthenticator authenticator = en.next();
encodedPassword = authenticator.encode(password); encodedPassword = authenticator.encode(password);
if (encodedPassword != null) { if (encodedPassword != null) {
@ -1733,7 +1742,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
} }
String encodedPassword = null; String encodedPassword = null;
for (UserAuthenticator authenticator : _userAuthenticators) { for (UserAuthenticator authenticator : _userPasswordEncoders) {
encodedPassword = authenticator.encode(password); encodedPassword = authenticator.encode(password);
if (encodedPassword != null) { if (encodedPassword != null) {
break; break;