// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by 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. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.utils.crypt; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Properties; import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig; import org.jasypt.exceptions.EncryptionOperationNotPossibleException; import org.jasypt.properties.EncryptableProperties; import com.cloud.utils.PropertiesUtil; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.PropertiesConfiguration; /* * EncryptionSecretKeyChanger updates Management Secret Key / DB Secret Key or both. * DB secret key is validated against the key in db.properties * db.properties is updated with values encrypted using new MS secret key * DB data migrated using new DB secret key */ public class EncryptionSecretKeyChanger { private StandardPBEStringEncryptor oldEncryptor = new StandardPBEStringEncryptor(); private StandardPBEStringEncryptor newEncryptor = new StandardPBEStringEncryptor(); private static final String keyFile = "/etc/cloud/management/key"; public static void main(String[] args){ List<String> argsList = Arrays.asList(args); Iterator<String> iter = argsList.iterator(); String oldMSKey = null; String oldDBKey = null; String newMSKey = null; String newDBKey = null; //Parse command-line args while (iter.hasNext()) { String arg = iter.next(); // Old MS Key if (arg.equals("-m")) { oldMSKey = iter.next(); } // Old DB Key if (arg.equals("-d")) { oldDBKey = iter.next(); } // New MS Key if (arg.equals("-n")) { newMSKey = iter.next(); } // New DB Key if (arg.equals("-e")) { newDBKey = iter.next(); } } if(oldMSKey == null || oldDBKey ==null){ System.out.println("Existing MS secret key or DB secret key is not provided"); usage(); return; } if(newMSKey == null && newDBKey ==null){ System.out.println("New MS secret key and DB secret are both not provided"); usage(); return; } final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties"); final Properties dbProps; EncryptionSecretKeyChanger keyChanger = new EncryptionSecretKeyChanger(); StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); keyChanger.initEncryptor(encryptor, oldMSKey); dbProps = new EncryptableProperties(encryptor); PropertiesConfiguration backupDBProps = null; System.out.println("Parsing db.properties file"); try { dbProps.load(new FileInputStream(dbPropsFile)); backupDBProps = new PropertiesConfiguration(dbPropsFile); } catch (FileNotFoundException e) { System.out.println("db.properties file not found while reading DB secret key" +e.getMessage()); } catch (IOException e) { System.out.println("Error while reading DB secret key from db.properties" +e.getMessage()); } catch (ConfigurationException e) { e.printStackTrace(); } String dbSecretKey = null; try { dbSecretKey = dbProps.getProperty("db.cloud.encrypt.secret"); } catch (EncryptionOperationNotPossibleException e) { System.out.println("Failed to decrypt existing DB secret key from db.properties. "+e.getMessage()); return; } if(!oldDBKey.equals(dbSecretKey)){ System.out.println("Incorrect MS Secret Key or DB Secret Key"); return; } System.out.println("Secret key provided matched the key in db.properties"); final String encryptionType = dbProps.getProperty("db.cloud.encryption.type"); if(newMSKey == null){ System.out.println("No change in MS Key. Skipping migrating db.properties"); } else { if(!keyChanger.migrateProperties(dbPropsFile, dbProps, newMSKey, newDBKey)){ System.out.println("Failed to update db.properties"); return; } else { //db.properties updated successfully if(encryptionType.equals("file")){ //update key file with new MS key try { FileWriter fwriter = new FileWriter(keyFile); BufferedWriter bwriter = new BufferedWriter(fwriter); bwriter.write(newMSKey); bwriter.close(); } catch (IOException e) { System.out.println("Failed to write new secret to file. Please update the file manually"); } } } } boolean success = false; if(newDBKey == null || newDBKey.equals(oldDBKey)){ System.out.println("No change in DB Secret Key. Skipping Data Migration"); } else { EncryptionSecretKeyChecker.initEncryptorForMigration(oldMSKey); try { success = keyChanger.migrateData(oldDBKey, newDBKey); } catch (Exception e) { System.out.println("Error during data migration"); e.printStackTrace(); success = false; } } if(success){ System.out.println("Successfully updated secret key(s)"); } else { System.out.println("Data Migration failed. Reverting db.properties"); //revert db.properties try { backupDBProps.save(); } catch (ConfigurationException e) { e.printStackTrace(); } if(encryptionType.equals("file")){ //revert secret key in file try { FileWriter fwriter = new FileWriter(keyFile); BufferedWriter bwriter = new BufferedWriter(fwriter); bwriter.write(oldMSKey); bwriter.close(); } catch (IOException e) { System.out.println("Failed to revert to old secret to file. Please update the file manually"); } } } } private boolean migrateProperties(File dbPropsFile, Properties dbProps, String newMSKey, String newDBKey){ System.out.println("Migrating db.properties.."); StandardPBEStringEncryptor msEncryptor = new StandardPBEStringEncryptor();; initEncryptor(msEncryptor, newMSKey); try { PropertiesConfiguration newDBProps = new PropertiesConfiguration(dbPropsFile); if(newDBKey!=null && !newDBKey.isEmpty()){ newDBProps.setProperty("db.cloud.encrypt.secret", "ENC("+msEncryptor.encrypt(newDBKey)+")"); } String prop = dbProps.getProperty("db.cloud.password"); if(prop!=null && !prop.isEmpty()){ newDBProps.setProperty("db.cloud.password", "ENC("+msEncryptor.encrypt(prop)+")"); } prop = dbProps.getProperty("db.usage.password"); if(prop!=null && !prop.isEmpty()){ newDBProps.setProperty("db.usage.password", "ENC("+msEncryptor.encrypt(prop)+")"); } newDBProps.save(dbPropsFile.getAbsolutePath()); } catch (Exception e) { e.printStackTrace(); return false; } System.out.println("Migrating db.properties Done."); return true; } private boolean migrateData(String oldDBKey, String newDBKey){ System.out.println("Begin Data migration"); initEncryptor(oldEncryptor, oldDBKey); initEncryptor(newEncryptor, newDBKey); System.out.println("Initialised Encryptors"); Transaction txn = Transaction.open("Migrate"); txn.start(); try { Connection conn; try { conn = txn.getConnection(); } catch (SQLException e) { throw new CloudRuntimeException("Unable to migrate encrypted data in the database", e); } migrateConfigValues(conn); migrateHostDetails(conn); migrateVNCPassword(conn); migrateUserCredentials(conn); txn.commit(); } finally { txn.close(); } System.out.println("End Data migration"); return true; } private void initEncryptor(StandardPBEStringEncryptor encryptor, String secretKey){ encryptor.setAlgorithm("PBEWithMD5AndDES"); SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig(); stringConfig.setPassword(secretKey); encryptor.setConfig(stringConfig); } private String migrateValue(String value){ if(value ==null || value.isEmpty()){ return value; } String decryptVal = oldEncryptor.decrypt(value); return newEncryptor.encrypt(decryptVal); } private void migrateConfigValues(Connection conn) { System.out.println("Begin migrate config values"); PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = conn.prepareStatement("select name, value from configuration where category in ('Hidden', 'Secure')"); rs = pstmt.executeQuery(); while (rs.next()) { String name = rs.getString(1); String value = rs.getString(2); if(value == null || value.isEmpty()){ continue; } String encryptedValue = migrateValue(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 to update configuration values ", e); } catch (UnsupportedEncodingException e) { throw new CloudRuntimeException("Unable to update configuration values ", e); } finally { try { if (rs != null) { rs.close(); } if (pstmt != null) { pstmt.close(); } } catch (SQLException e) { } } System.out.println("End migrate config values"); } private void migrateHostDetails(Connection conn) { System.out.println("Begin migrate host details"); PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = conn.prepareStatement("select id, value from host_details where name = 'password'"); rs = pstmt.executeQuery(); while (rs.next()) { long id = rs.getLong(1); String value = rs.getString(2); if(value == null || value.isEmpty()){ continue; } String encryptedValue = migrateValue(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 update host_details values ", e); } catch (UnsupportedEncodingException e) { throw new CloudRuntimeException("Unable update host_details values ", e); } finally { try { if (rs != null) { rs.close(); } if (pstmt != null) { pstmt.close(); } } catch (SQLException e) { } } System.out.println("End migrate host details"); } private void migrateVNCPassword(Connection conn) { System.out.println("Begin migrate VNC password"); 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 || value.isEmpty()){ continue; } String encryptedValue = migrateValue(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 update vm_instance vnc_password ", e); } catch (UnsupportedEncodingException e) { throw new CloudRuntimeException("Unable update vm_instance vnc_password ", e); } finally { try { if (rs != null) { rs.close(); } if (pstmt != null) { pstmt.close(); } } catch (SQLException e) { } } System.out.println("End migrate VNC password"); } private void migrateUserCredentials(Connection conn) { System.out.println("Begin migrate user credentials"); PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = conn.prepareStatement("select id, secret_key from user"); rs = pstmt.executeQuery(); while (rs.next()) { long id = rs.getLong(1); String secretKey = rs.getString(2); if(secretKey == null || secretKey.isEmpty()){ continue; } String encryptedSecretKey = migrateValue(secretKey); pstmt = conn.prepareStatement("update user set secret_key=? where id=?"); pstmt.setBytes(1, encryptedSecretKey.getBytes("UTF-8")); pstmt.setLong(2, id); pstmt.executeUpdate(); } } catch (SQLException e) { throw new CloudRuntimeException("Unable update user secret key ", e); } catch (UnsupportedEncodingException e) { throw new CloudRuntimeException("Unable update user secret key ", e); } finally { try { if (rs != null) { rs.close(); } if (pstmt != null) { pstmt.close(); } } catch (SQLException e) { } } System.out.println("End migrate user credentials"); } private static void usage(){ System.out.println("Usage: \tEncryptionSecretKeyChanger \n" + "\t\t-m <Mgmt Secret Key> \n" + "\t\t-d <DB Secret Key> \n" + "\t\t-n [New Mgmt Secret Key] \n" + "\t\t-e [New DB Secret Key]"); } }