diff --git a/client/tomcatconf/components-premium.xml.in b/client/tomcatconf/components-premium.xml.in index 9472e76744d..366383fe672 100755 --- a/client/tomcatconf/components-premium.xml.in +++ b/client/tomcatconf/components-premium.xml.in @@ -2,6 +2,7 @@ + diff --git a/client/tomcatconf/components.xml.in b/client/tomcatconf/components.xml.in index c6307bfac6e..9a1a4ccc7b0 100755 --- a/client/tomcatconf/components.xml.in +++ b/client/tomcatconf/components.xml.in @@ -25,6 +25,7 @@ + diff --git a/client/tomcatconf/db.properties.in b/client/tomcatconf/db.properties.in index f3a01a9835b..698ce41270a 100644 --- a/client/tomcatconf/db.properties.in +++ b/client/tomcatconf/db.properties.in @@ -33,6 +33,9 @@ db.cloud.keyStorePassword= db.cloud.trustStore= db.cloud.trustStorePassword= +# Encryption Settings +db.cloud.encryption.type=none +db.cloud.encrypt.secret= # usage database settings db.usage.username=@DBUSER@ diff --git a/core/src/com/cloud/configuration/ConfigurationVO.java b/core/src/com/cloud/configuration/ConfigurationVO.java index 500f1ea701e..95f32fd04db 100644 --- a/core/src/com/cloud/configuration/ConfigurationVO.java +++ b/core/src/com/cloud/configuration/ConfigurationVO.java @@ -36,7 +36,7 @@ public class ConfigurationVO implements Configuration{ @Column(name="name") private String name; - @Column(name="value", length=4095) + @Column(name="value", length=4095, encryptable=true) private String value; @Column(name="description", length=1024) diff --git a/core/src/com/cloud/host/DetailVO.java b/core/src/com/cloud/host/DetailVO.java index 021590a3c90..d87081f25cb 100644 --- a/core/src/com/cloud/host/DetailVO.java +++ b/core/src/com/cloud/host/DetailVO.java @@ -38,7 +38,7 @@ public class DetailVO { @Column(name="name") private String name; - @Column(name="value") + @Column(name="value", encryptable=true) private String value; protected DetailVO() { diff --git a/core/src/com/cloud/network/VpnUserVO.java b/core/src/com/cloud/network/VpnUserVO.java index 586fb690db4..9bd869e5985 100644 --- a/core/src/com/cloud/network/VpnUserVO.java +++ b/core/src/com/cloud/network/VpnUserVO.java @@ -44,7 +44,7 @@ public class VpnUserVO implements VpnUser { @Column(name="username") private String username; - @Column(name="password") + @Column(name="password", encryptable=true) private String password; @Column(name="state") diff --git a/core/src/com/cloud/user/UserAccountVO.java b/core/src/com/cloud/user/UserAccountVO.java index af578d76636..69f4cb747fd 100644 --- a/core/src/com/cloud/user/UserAccountVO.java +++ b/core/src/com/cloud/user/UserAccountVO.java @@ -44,7 +44,7 @@ public class UserAccountVO implements UserAccount { @Column(name="username") private String username = null; - @Column(name="password") + @Column(name="password", encryptable=true) private String password = null; @Column(name="firstname") diff --git a/core/src/com/cloud/user/UserVO.java b/core/src/com/cloud/user/UserVO.java index c5fc3b70250..dcd7578c059 100644 --- a/core/src/com/cloud/user/UserVO.java +++ b/core/src/com/cloud/user/UserVO.java @@ -50,7 +50,7 @@ public class UserVO implements User { @Column(name = "username") private String username = null; - @Column(name = "password") + @Column(name = "password", encryptable=true) private String password = null; @Column(name = "firstname") @@ -69,10 +69,10 @@ public class UserVO implements User { @Enumerated(value=EnumType.STRING) private State state; - @Column(name = "api_key") + @Column(name = "api_key", encryptable=true) private String apiKey = null; - @Column(name = "secret_key") + @Column(name = "secret_key", encryptable=true) private String secretKey = null; @Column(name = GenericDao.CREATED_COLUMN) diff --git a/core/src/com/cloud/vm/VMInstanceVO.java b/core/src/com/cloud/vm/VMInstanceVO.java index df02d666230..8c28a31d892 100644 --- a/core/src/com/cloud/vm/VMInstanceVO.java +++ b/core/src/com/cloud/vm/VMInstanceVO.java @@ -56,7 +56,7 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject + diff --git a/deps/cloud-jasypt-1.8.jar b/deps/cloud-jasypt-1.8.jar new file mode 100644 index 00000000000..daaed002a46 Binary files /dev/null and b/deps/cloud-jasypt-1.8.jar differ diff --git a/server/src/com/cloud/configuration/dao/ConfigurationDaoImpl.java b/server/src/com/cloud/configuration/dao/ConfigurationDaoImpl.java index e6505e55fee..8bf4754391c 100644 --- a/server/src/com/cloud/configuration/dao/ConfigurationDaoImpl.java +++ b/server/src/com/cloud/configuration/dao/ConfigurationDaoImpl.java @@ -32,6 +32,7 @@ import org.apache.log4j.Logger; import com.cloud.configuration.ConfigurationVO; import com.cloud.storage.DiskOfferingVO; +import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; @@ -126,7 +127,7 @@ public class ConfigurationDaoImpl extends GenericDaoBase. + * + */ +package com.cloud.upgrade.dao; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +import org.apache.log4j.Logger; + +import com.cloud.utils.crypt.DBEncryptionUtil; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +public class Upgrade2213to30 implements DbUpgrade { + final static Logger s_logger = Logger.getLogger(Upgrade2213to30.class); + + @Override + public String[] getUpgradableVersionRange() { + return new String[] { "2.2.13", "3.0.0"}; + } + + @Override + public String getUpgradedVersion() { + return "3.0.0"; + } + + @Override + public boolean supportsRollingUpgrade() { + return true; + } + + @Override + public File[] getPrepareScripts() { + String script = Script.findScript("", "db/db/schema-2213to30.sql"); + if (script == null) { + throw new CloudRuntimeException("Unable to find db/schema-2213to30.sql"); + } + + return new File[] { new File(script) }; + } + + @Override + public void performDataMigration(Connection conn) { + encryptData(conn); + } + + @Override + public File[] getCleanupScripts() { + return null; + } + + + private void encryptData(Connection conn) { + encryptConfigValues(conn); + encryptHostDetails(conn); + encryptVNCPassword(conn); + encryptUserCredentials(conn); + } + + private void encryptConfigValues(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + pstmt = conn.prepareStatement("select name, value from configuration"); + rs = pstmt.executeQuery(); + while (rs.next()) { + String name = rs.getString(1); + String value = rs.getString(2); + if(value == null){ + continue; + } + String encryptedValue = DBEncryptionUtil.encrypt(value); + pstmt = conn.prepareStatement("update configuration set value=? where name=?"); + pstmt.setBytes(1, encryptedValue.getBytes("UTF-8")); + pstmt.setString(2, name); + pstmt.executeUpdate(); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable encrypt configuration values"); + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Unable encrypt configuration values"); + } finally { + try { + if (rs != null) { + rs.close(); + } + + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } + + private void encryptHostDetails(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + pstmt = conn.prepareStatement("select id, value from host_details"); + rs = pstmt.executeQuery(); + while (rs.next()) { + long id = rs.getLong(1); + String value = rs.getString(2); + if(value == null){ + continue; + } + String encryptedValue = DBEncryptionUtil.encrypt(value); + pstmt = conn.prepareStatement("update host_details set value=? where id=?"); + pstmt.setBytes(1, encryptedValue.getBytes("UTF-8")); + pstmt.setLong(2, id); + pstmt.executeUpdate(); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable encrypt host_details values"); + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Unable encrypt configuration values"); + } finally { + try { + if (rs != null) { + rs.close(); + } + + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } + + private void encryptVNCPassword(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + pstmt = conn.prepareStatement("select id, vnc_password from vm_instance"); + rs = pstmt.executeQuery(); + while (rs.next()) { + long id = rs.getLong(1); + String value = rs.getString(2); + if(value == null){ + continue; + } + String encryptedValue = DBEncryptionUtil.encrypt(value); + pstmt = conn.prepareStatement("update vm_instance set vnc_password=? where id=?"); + pstmt.setBytes(1, encryptedValue.getBytes("UTF-8")); + pstmt.setLong(2, id); + pstmt.executeUpdate(); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable encrypt vm_instance vnc_password"); + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Unable encrypt configuration values"); + } finally { + try { + if (rs != null) { + rs.close(); + } + + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } + + private void encryptUserCredentials(Connection conn) { + PreparedStatement pstmt = null; + ResultSet rs = null; + try { + pstmt = conn.prepareStatement("select id, password, api_key, secret_key from user"); + rs = pstmt.executeQuery(); + while (rs.next()) { + long id = rs.getLong(1); + String password = rs.getString(2); + String encryptedPassword = DBEncryptionUtil.encrypt(password); + String apiKey = rs.getString(3); + String encryptedApiKey = DBEncryptionUtil.encrypt(apiKey); + String secretKey = rs.getString(4); + String encryptedSecretKey = DBEncryptionUtil.encrypt(secretKey); + pstmt = conn.prepareStatement("update user set password=?, api_key=?, secret_key=? where id=?"); + if(encryptedPassword == null){ + pstmt.setNull(1, Types.VARCHAR); + } else { + pstmt.setBytes(1, encryptedPassword.getBytes("UTF-8")); + } + if(encryptedApiKey == null){ + pstmt.setNull(2, Types.VARCHAR); + } else { + pstmt.setBytes(2, encryptedApiKey.getBytes("UTF-8")); + } + if(encryptedSecretKey == null){ + pstmt.setNull(3, Types.VARCHAR); + } else { + pstmt.setBytes(3, encryptedSecretKey.getBytes("UTF-8")); + } + pstmt.setLong(4, id); + pstmt.executeUpdate(); + } + } catch (SQLException e) { + throw new CloudRuntimeException("Unable encrypt user credentials"); + } catch (UnsupportedEncodingException e) { + throw new CloudRuntimeException("Unable encrypt configuration values"); + } finally { + try { + if (rs != null) { + rs.close(); + } + + if (pstmt != null) { + pstmt.close(); + } + } catch (SQLException e) { + } + } + } +} diff --git a/setup/db/db/schema-2212to30.sql b/setup/db/db/schema-2213to30.sql similarity index 98% rename from setup/db/db/schema-2212to30.sql rename to setup/db/db/schema-2213to30.sql index dae28eeec6d..37c6e36b59d 100644 --- a/setup/db/db/schema-2212to30.sql +++ b/setup/db/db/schema-2213to30.sql @@ -1,5 +1,5 @@ --; --- Schema upgrade from 2.2.12 to 3.0; +-- Schema upgrade from 2.2.x to 3.0; --; ALTER TABLE `cloud`.`template_host_ref` DROP COLUMN `pool_id`; diff --git a/utils/src/com/cloud/utils/component/ComponentLocator.java b/utils/src/com/cloud/utils/component/ComponentLocator.java index 489b2619bd3..4a2598121b6 100755 --- a/utils/src/com/cloud/utils/component/ComponentLocator.java +++ b/utils/src/com/cloud/utils/component/ComponentLocator.java @@ -95,7 +95,7 @@ public class ComponentLocator implements ComponentLocatorMBean { protected HashMap> _adapterMap; protected HashMap> _managerMap; - protected HashMap> _checkerMap; + protected LinkedHashMap> _checkerMap; protected LinkedHashMap>> _daoMap; protected String _serverName; protected Object _component; @@ -131,7 +131,7 @@ public class ComponentLocator implements ComponentLocatorMBean { SAXParser saxParser = spfactory.newSAXParser(); _daoMap = new LinkedHashMap>>(); _managerMap = new LinkedHashMap>(); - _checkerMap = new HashMap>(); + _checkerMap = new LinkedHashMap>(); _adapterMap = new HashMap>(); _factories = new HashMap, Class>(); File file = PropertiesUtil.findConfigFile(filename); @@ -866,7 +866,7 @@ public class ComponentLocator implements ComponentLocatorMBean { protected class XmlHandler extends DefaultHandler { public HashMap>> adapters; public HashMap> managers; - public HashMap> checkers; + public LinkedHashMap> checkers; public LinkedHashMap>> daos; public String parent; public String library; @@ -884,7 +884,7 @@ public class ComponentLocator implements ComponentLocatorMBean { parse = false; adapters = new HashMap>>(); managers = new HashMap>(); - checkers = new HashMap>(); + checkers = new LinkedHashMap>(); daos = new LinkedHashMap>>(); value = null; parent = null; diff --git a/utils/src/com/cloud/utils/crypt/DBEncryptionUtil.java b/utils/src/com/cloud/utils/crypt/DBEncryptionUtil.java new file mode 100755 index 00000000000..7d07bd3fac2 --- /dev/null +++ b/utils/src/com/cloud/utils/crypt/DBEncryptionUtil.java @@ -0,0 +1,84 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.crypt; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Properties; + +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.properties.EncryptableProperties; + +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.exception.CloudRuntimeException; + +public class DBEncryptionUtil { + + private static StandardPBEStringEncryptor s_encryptor = null; + + public static String encrypt(String plain){ + if(!EncryptionSecretKeyChecker.useEncryption()){ + return plain; + } + //synchornize ?? + if(s_encryptor == null){ + initialize(); + } + return s_encryptor.encrypt(plain); + } + + public static String decrypt(String encrypted){ + if(!EncryptionSecretKeyChecker.useEncryption()){ + return encrypted; + } + if(s_encryptor == null){ + initialize(); + } + return s_encryptor.decrypt(encrypted); + } + + private static void initialize(){ + final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); + final Properties dbProps; + + if(EncryptionSecretKeyChecker.useEncryption()){ + StandardPBEStringEncryptor encryptor = EncryptionSecretKeyChecker.getEncryptor(); + dbProps = new EncryptableProperties(encryptor); + try { + dbProps.load(new FileInputStream(dbPropsFile)); + } catch (FileNotFoundException e) { + throw new CloudRuntimeException("db.properties file not found while reading DB secret key", e); + } catch (IOException e) { + throw new CloudRuntimeException("Erroe while reading DB secret key from db.properties", e); + } + + String dbSecretKey = dbProps.getProperty("db.cloud.encrypt.secret"); + if(dbSecretKey == null || dbSecretKey.isEmpty()){ + throw new CloudRuntimeException("Empty DB secret key in db.properties"); + } + + s_encryptor = new StandardPBEStringEncryptor(); + s_encryptor.setAlgorithm("PBEWithMD5AndDES"); + s_encryptor.setPassword(dbSecretKey); + } else { + throw new CloudRuntimeException("Trying to encrypt db values when encrytion is not enabled"); + } + } +} diff --git a/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java new file mode 100755 index 00000000000..230b8be6dbc --- /dev/null +++ b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java @@ -0,0 +1,140 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.crypt; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Properties; + +import javax.ejb.Local; + +import org.apache.log4j.Logger; +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; + +import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.component.SystemIntegrityChecker; +import com.cloud.utils.exception.CloudRuntimeException; + +@Local(value = {SystemIntegrityChecker.class}) +public class EncryptionSecretKeyChecker implements SystemIntegrityChecker { + + private static final Logger s_logger = Logger.getLogger(EncryptionSecretKeyChecker.class); + + private static final String s_keyFile = "/etc/cloud/management/key"; + private static final String s_envKey = "CLOUD_SECRET_KEY"; + private static StandardPBEStringEncryptor s_encryptor = new StandardPBEStringEncryptor(); + private static boolean s_useEncryption = false; + + @Override + public void check() { + //Get encryption type from db.properties + final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); + final Properties dbProps = new Properties(); + try { + dbProps.load(new FileInputStream(dbPropsFile)); + + final String encryptionType = dbProps.getProperty("db.cloud.encryption.type"); + + s_logger.debug("Encryption Type: "+ encryptionType); + + if(encryptionType == null || encryptionType.equals("none")){ + return; + } + + s_encryptor.setAlgorithm("PBEWithMD5AndDES"); + String secretKey = null; + + SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig(); + + if(encryptionType.equals("file")){ + try { + BufferedReader in = new BufferedReader(new FileReader(s_keyFile)); + secretKey = in.readLine(); + //Check for null or empty secret key + } catch (FileNotFoundException e) { + throw new CloudRuntimeException("File containing secret key not found: "+s_keyFile, e); + } catch (IOException e) { + throw new CloudRuntimeException("Error while reading secret key from: "+s_keyFile, e); + } + + if(secretKey == null || secretKey.isEmpty()){ + throw new CloudRuntimeException("Secret key is null or empty in file "+s_keyFile); + } + + } else if(encryptionType.equals("env")){ + secretKey = System.getenv(s_envKey); + if(secretKey == null || secretKey.isEmpty()){ + throw new CloudRuntimeException("Environment variable "+s_envKey+" is not set or empty"); + } + } else if(encryptionType.equals("web")){ + ServerSocket serverSocket = null; + int port = 8097; + try { + serverSocket = new ServerSocket(port); + } catch (IOException ioex) { + throw new CloudRuntimeException("Error initializing secret key reciever", ioex); + } + s_logger.info("Waiting for admin to send secret key on port "+port); + Socket clientSocket = null; + try { + clientSocket = serverSocket.accept(); + } catch (IOException e) { + throw new CloudRuntimeException("Accept failed on "+port); + } + PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); + BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + String inputLine, outputLine; + if ((inputLine = in.readLine()) != null) { + s_logger.info("Input : "+inputLine); + secretKey = inputLine; + } + out.close(); + in.close(); + clientSocket.close(); + serverSocket.close(); + } else { + throw new CloudRuntimeException("Invalid encryption type: "+encryptionType); + } + + stringConfig.setPassword(secretKey); + s_encryptor.setConfig(stringConfig); + s_useEncryption = true; + } catch (FileNotFoundException e) { + throw new CloudRuntimeException("File db.properties not found", e); + } catch (IOException e) { + throw new CloudRuntimeException("Error while reading db.properties", e); + } + } + + public static StandardPBEStringEncryptor getEncryptor() { + return s_encryptor; + } + + public static boolean useEncryption(){ + return s_useEncryption; + } +} diff --git a/utils/src/com/cloud/utils/crypt/EncryptionSecretKeySender.java b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeySender.java new file mode 100755 index 00000000000..4e2b336378e --- /dev/null +++ b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeySender.java @@ -0,0 +1,65 @@ +/** + * Copyright (C) 2011 Citrix Systems, Inc. All rights reserved + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package com.cloud.utils.crypt; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.Socket; + +import com.cloud.utils.NumbersUtil; + + +public class EncryptionSecretKeySender { + public static void main(String args[]){ + try { + + // Create a socket to the host + String hostname = "localhost"; + int port = 8097; + + if(args.length == 2){ + hostname = args[0]; + port = NumbersUtil.parseInt(args[1], port); + } + + + InetAddress addr = InetAddress.getByName(hostname); + Socket socket = new Socket(addr, port); + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + BufferedReader in = new BufferedReader(new InputStreamReader( + socket.getInputStream())); + java.io.BufferedReader stdin = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); + String validationWord = "cloudnine"; + String validationInput = ""; + while(!validationWord.equals(validationInput)){ + System.out.print("Enter Validation Word:"); + validationInput = stdin.readLine(); + System.out.println(); + } + System.out.print("Enter Secret Key:"); + String input = stdin.readLine(); + if (input != null) { + out.println(input); + } + } catch (Exception e) { + System.out.print("Exception while sending secret key "+e); + } + } +} \ No newline at end of file diff --git a/utils/src/com/cloud/utils/db/Attribute.java b/utils/src/com/cloud/utils/db/Attribute.java index 38ad512b585..4391cd7fd6c 100755 --- a/utils/src/com/cloud/utils/db/Attribute.java +++ b/utils/src/com/cloud/utils/db/Attribute.java @@ -54,7 +54,8 @@ public class Attribute { DC(0x40000), CharDT(0x100000), StringDT(0x200000), - IntegerDT(0x400000); + IntegerDT(0x400000), + Encrypted(0x800000); int place; Flag(int place) { @@ -144,6 +145,9 @@ public class Attribute { if (column == null || column.nullable()) { flags = Flag.Nullable.setTrue(flags); } + if (column != null && column.encryptable()) { + flags = Flag.Encrypted.setTrue(flags); + } } ElementCollection ec = field.getAnnotation(ElementCollection.class); if (ec != null) { @@ -201,6 +205,10 @@ public class Attribute { flags = flag.setFalse(flags); } + public final boolean isEncrypted() { + return Flag.Encrypted.check(flags); + } + public Field getField() { return field; } diff --git a/utils/src/com/cloud/utils/db/GenericDaoBase.java b/utils/src/com/cloud/utils/db/GenericDaoBase.java index 9240b4083ad..49a7e8142ba 100755 --- a/utils/src/com/cloud/utils/db/GenericDaoBase.java +++ b/utils/src/com/cloud/utils/db/GenericDaoBase.java @@ -68,6 +68,7 @@ import com.cloud.utils.DateUtil; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; +import com.cloud.utils.crypt.DBEncryptionUtil; import com.cloud.utils.db.SearchCriteria.SelectType; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Ip; @@ -498,7 +499,11 @@ public abstract class GenericDaoBase implements Gene byte[] bytes = rs.getBytes(index); if(bytes != null) { try { - field.set(entity, new String(bytes, "UTF-8")); + if(field.getAnnotation(Column.class).encryptable()){ + field.set(entity, DBEncryptionUtil.decrypt(new String(bytes, "UTF-8"))); + } else { + field.set(entity, new String(bytes, "UTF-8")); + } } catch (IllegalArgumentException e) { assert(false); throw new CloudRuntimeException("IllegalArgumentException when converting UTF-8 data"); @@ -1363,7 +1368,6 @@ public abstract class GenericDaoBase implements Gene return; } } - if (attr.field.getType() == String.class) { final String str = (String)value; if (str == null) { @@ -1376,7 +1380,11 @@ public abstract class GenericDaoBase implements Gene // to support generic localization, utilize MySql UTF-8 support if (length < str.length()) { try { - pstmt.setBytes(j, str.substring(0, column.length()).getBytes("UTF-8")); + if (attr.is(Attribute.Flag.Encrypted)){ + pstmt.setBytes(j, DBEncryptionUtil.encrypt(str.substring(0, column.length())).getBytes("UTF-8")); + } else { + pstmt.setBytes(j, str.substring(0, column.length()).getBytes("UTF-8")); + } } catch (UnsupportedEncodingException e) { // no-way it can't support UTF-8 encoding assert(false); @@ -1384,7 +1392,11 @@ public abstract class GenericDaoBase implements Gene } } else { try { - pstmt.setBytes(j, str.getBytes("UTF-8")); + if (attr.is(Attribute.Flag.Encrypted)){ + pstmt.setBytes(j, DBEncryptionUtil.encrypt(str).getBytes("UTF-8")); + } else { + pstmt.setBytes(j, str.getBytes("UTF-8")); + } } catch (UnsupportedEncodingException e) { // no-way it can't support UTF-8 encoding assert(false); diff --git a/utils/src/com/cloud/utils/db/Transaction.java b/utils/src/com/cloud/utils/db/Transaction.java index a7b4aa45b24..7cffe0db54a 100755 --- a/utils/src/com/cloud/utils/db/Transaction.java +++ b/utils/src/com/cloud/utils/db/Transaction.java @@ -41,9 +41,12 @@ import org.apache.commons.pool.KeyedObjectPoolFactory; import org.apache.commons.pool.impl.GenericObjectPool; import org.apache.commons.pool.impl.StackKeyedObjectPoolFactory; import org.apache.log4j.Logger; +import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; +import org.jasypt.properties.EncryptableProperties; import com.cloud.utils.Pair; import com.cloud.utils.PropertiesUtil; +import com.cloud.utils.crypt.EncryptionSecretKeyChecker; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.mgmt.JmxUtil; @@ -954,9 +957,16 @@ public class Transaction { static { try { final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); - final Properties dbProps = new Properties(); + final Properties dbProps; + + if(EncryptionSecretKeyChecker.useEncryption()){ + StandardPBEStringEncryptor encryptor = EncryptionSecretKeyChecker.getEncryptor(); + dbProps = new EncryptableProperties(encryptor); + } else { + dbProps = new Properties(); + } dbProps.load(new FileInputStream(dbPropsFile)); - + // FIXME: If params are missing...default them???? final int cloudMaxActive = Integer.parseInt(dbProps.getProperty("db.cloud.maxActive")); final int cloudMaxIdle = Integer.parseInt(dbProps.getProperty("db.cloud.maxIdle")); diff --git a/utils/src/javax/persistence/Column.java b/utils/src/javax/persistence/Column.java index d3d8774b0a2..13606f2510a 100644 --- a/utils/src/javax/persistence/Column.java +++ b/utils/src/javax/persistence/Column.java @@ -137,4 +137,8 @@ public @interface Column { * (Applies only if a decimal column is used.) */ int scale() default 0; + /** + * (Optional) Whether encryption should be used for this column + */ + boolean encryptable() default false; }