Improve console access security with random generated hash key

This commit is contained in:
Kelven Yang 2010-11-12 16:59:28 -08:00
parent a0e4b927f0
commit 0653d6d7f6
7 changed files with 69 additions and 10 deletions

View File

@ -57,7 +57,10 @@ public interface ConfigurationDao extends GenericDao<ConfigurationVO, String> {
* Gets the value for the specified configuration name
* @return value
*/
public String getValue(String name);
public String getValue(String name);
public String getValueAndInitIfNotExist(String name, String initValue);
/**
* returns whether or not this is a premium configuration
@ -65,5 +68,5 @@ public interface ConfigurationDao extends GenericDao<ConfigurationVO, String> {
*/
boolean isPremium();
ConfigurationVO findByName(String name);
ConfigurationVO findByName(String name);
}

View File

@ -19,6 +19,7 @@
package com.cloud.configuration.dao;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -29,10 +30,12 @@ import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import com.cloud.configuration.ConfigurationVO;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.exception.CloudRuntimeException;
@Local(value={ConfigurationDao.class})
public class ConfigurationDaoImpl extends GenericDaoBase<ConfigurationVO, String> implements ConfigurationDao {
@ -144,7 +147,36 @@ public class ConfigurationDaoImpl extends GenericDaoBase<ConfigurationVO, String
ConfigurationVO config = configurations.get(0);
String value = config.getValue();
return value;
}
@Override
@DB
public String getValueAndInitIfNotExist(String name, String initValue) {
Transaction txn = Transaction.currentTxn();
PreparedStatement stmt = null;
PreparedStatement stmtInsert = null;
String returnValue = initValue;
try {
txn.start();
stmt = txn.prepareAutoCloseStatement("SELECT value FROM configuration WHERE name=?");
stmt.setString(1, name);
ResultSet rs = stmt.executeQuery();
if(rs != null && rs.next()) {
returnValue = rs.getString(1);
} else {
stmtInsert = txn.prepareAutoCloseStatement("INSERT INTO configuration(instance, name, value, description) VALUES('DEFAULT', ?, ?, '')");
stmtInsert.setString(1, name);
stmtInsert.setString(2, initValue);
if(stmtInsert.executeUpdate() < 1) {
throw new CloudRuntimeException("Unable to init configuration variable: " + name);
}
}
txn.commit();
return returnValue;
} catch (Exception e) {
s_logger.warn("Unable to update Configuration Value", e);
throw new CloudRuntimeException("Unable to init configuration variable: " + name);
}
}
@Override

View File

@ -191,8 +191,9 @@ public enum Config {
CreatePoolsInPod("Hidden", ManagementServer.class, Boolean.class, "xen.create.pools.in.pod", "false", "Should we automatically add XenServers into pools that are inside a Pod", null),
CloudIdentifier("Hidden", ManagementServer.class, String.class, "cloud.identifier", null, "A unique identifier for the cloud.", null),
SSOKey("Hidden", ManagementServer.class, String.class, "security.singlesignon.key", null, "A Single Sign-On key used for logging into the cloud", null),
SSOAuthTolerance("Advanced", ManagementServer.class, Long.class, "security.singlesignon.tolerance.millis", "300000", "The allowable clock difference in milliseconds between when an SSO login request is made and when it is received.", null);
SSOAuthTolerance("Advanced", ManagementServer.class, Long.class, "security.singlesignon.tolerance.millis", "300000", "The allowable clock difference in milliseconds between when an SSO login request is made and when it is received.", null),
HashKey("Hidden", ManagementServer.class, String.class, "security.hash.key", null, "for generic key-ed hash", null);
private final String _category;
private final Class<?> _componentClass;
private final Class<?> _type;

View File

@ -1299,8 +1299,14 @@ public class ConsoleProxyManagerImpl implements ConsoleProxyManager, VirtualMach
String ticket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId());
if(!ticket.startsWith(ticketInUrl)) {
s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId());
return new ConsoleAccessAuthenticationAnswer(cmd, false);
Date now = new Date();
// considering of minute round-up
String minuteEarlyTicket = ConsoleProxyServlet.genAccessTicket(cmd.getHost(), cmd.getPort(), cmd.getSid(), cmd.getVmId(),
new Date(now.getTime() - 60*1000));
if(!minuteEarlyTicket.startsWith(ticketInUrl)) {
s_logger.error("Access ticket expired or has been modified. vmId: " + cmd.getVmId());
return new ConsoleAccessAuthenticationAnswer(cmd, false);
}
}
if (cmd.getVmId() != null && cmd.getVmId().isEmpty()) {

View File

@ -1047,4 +1047,5 @@ public interface ManagementServer {
public List<RemoteAccessVpnVO> searchForRemoteAccessVpns(ListRemoteAccessVpnsCmd cmd);
public List<VpnUserVO> searchForVpnUsers(ListVpnUsersCmd cmd);
public String getHashKey();
}

View File

@ -48,6 +48,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@ -400,6 +401,7 @@ public class ManagementServerImpl implements ManagementServer {
private boolean _networkGroupsEnabled = false;
private boolean _isHypervisorSnapshotCapable = false;
private String _hashKey = null;
protected ManagementServerImpl() {
ComponentLocator locator = ComponentLocator.getLocator(Name);
@ -5765,4 +5767,14 @@ public class ManagementServerImpl implements ManagementServer {
return _vpnUsersDao.search(sc, searchFilter);
}
@Override
public String getHashKey() {
// although we may have race conditioning here, database transaction serialization should
// give us the same key
if(_hashKey == null) {
_hashKey = _configDao.getValueAndInitIfNotExist(Config.HashKey.key(), UUID.randomUUID().toString());
}
return _hashKey;
}
}

View File

@ -58,7 +58,7 @@ public class ConsoleProxyServlet extends HttpServlet {
private static final int DEFAULT_THUMBNAIL_WIDTH = 144;
private static final int DEFAULT_THUMBNAIL_HEIGHT = 110;
private final ManagementServer _ms = (ManagementServer)ComponentLocator.getComponent(ManagementServer.Name);
private final static ManagementServer _ms = (ManagementServer)ComponentLocator.getComponent(ManagementServer.Name);
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
@ -298,14 +298,18 @@ public class ConsoleProxyServlet extends HttpServlet {
}
public static String genAccessTicket(String host, String port, String sid, String tag) {
return genAccessTicket(host, port, sid, tag, new Date());
}
public static String genAccessTicket(String host, String port, String sid, String tag, Date normalizedHashTime) {
String params = "host=" + host + "&port=" + port + "&sid=" + sid + "&tag=" + tag;
try {
Mac mac = Mac.getInstance("HmacSHA1");
long ts = (new Date()).getTime();
long ts = normalizedHashTime.getTime();
ts = ts/60000; // round up to 1 minute
String secretKey = "cloud.com";
String secretKey = _ms.getHashKey();
SecretKeySpec keySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA1");
mac.init(keySpec);