Author: acoburn
Date: Mon Sep 24 20:05:17 2012
New Revision: 1389548

URL: http://svn.apache.org/viewvc?rev=1389548&view=rev
Log:
VCL-560

For installations using the vmprofile.password value in the database, this 
value is stored in cleartext. That means that if there are multiple management 
nodes, each
management node will have access to this password, which may not be desirable. 
The password is also passed in clear text along the network between the mysql 
database and the
management node(s). 

This implements the ability to set an asymmetric key so that the password can 
be set in the website but only be decypherable from the management node(s) with 
the private key.


Modified:
    vcl/trunk/managementnode/bin/install_perl_libs.pl
    vcl/trunk/managementnode/lib/VCL/utils.pm
    vcl/trunk/mysql/update-vcl.sql
    vcl/trunk/mysql/vcl.sql
    vcl/trunk/web/.ht-inc/utils.php
    vcl/trunk/web/.ht-inc/vm.php
    vcl/trunk/web/js/vm.js

Modified: vcl/trunk/managementnode/bin/install_perl_libs.pl
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/bin/install_perl_libs.pl?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/managementnode/bin/install_perl_libs.pl (original)
+++ vcl/trunk/managementnode/bin/install_perl_libs.pl Mon Sep 24 20:05:17 2012
@@ -192,6 +192,7 @@ sub install_linux_packages {
                'openssl-devel',
                'perl-Archive-Tar',
                'perl-CPAN',
+        'perl-Crypt-OpenSSL-RSA',
                'perl-DBD-MySQL',
                'perl-DBI',
                'perl-Digest-SHA1',

Modified: vcl/trunk/managementnode/lib/VCL/utils.pm
URL: 
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/utils.pm Mon Sep 24 20:05:17 2012
@@ -74,6 +74,7 @@ use Data::Dumper;
 use Cwd;
 use Sys::Hostname;
 use XML::Simple;
+use Crypt::OpenSSL::RSA;
 
 #use Date::Calc qw(Delta_DHMS Time_to_Date Date_to_Time);
 
@@ -4785,6 +4786,34 @@ EOF
        $vmhost_info->{vmprofile}{username} = '' if 
!$vmhost_info->{vmprofile}{username};
        $vmhost_info->{vmprofile}{password} = '' if 
!$vmhost_info->{vmprofile}{password};
        
+    # Decrypt the vmhost password
+    if(-f $vmhost_info->{vmprofile}{rsakey} &&
+            $vmhost_info->{vmprofile}{encryptedpasswd}){
+        # Read the private keyfile into a string
+        local $/ = undef;
+        open FH, $vmhost_info->{vmprofile}{rsakey} or
+                notify($ERRORS{'WARNING'}, 0, "Could not read private keyfile 
(" . $vmhost_info->{vmprofile}{rsakey} . "): $!");
+        my $key = <FH>;
+        close FH;
+        if($key){
+            my $encrypted = $vmhost_info->{vmprofile}{encryptedpasswd};
+            my $rsa = Crypt::OpenSSL::RSA->new_private_key($key);
+            # Croak on an invalid key
+            $rsa->check_key;
+            # Use the same padding algorithm as the PHP code
+            $rsa->use_pkcs1_oaep_padding;
+            # Convert password from hex to binary, decrypt
+            # and store in the vmprofile.password field
+            $vmhost_info->{vmprofile}{password} = $rsa->decrypt(pack("H*", 
$encrypted));
+            notify($ERRORS{'DEBUG'}, 0, "decrypted vmprofile password with 
key: " . $vmhost_info->{vmprofile}{rsakey});
+        } else {
+            notify($ERRORS{'WARNING'}, 0, "unable to decrypt vmprofile 
password");    
+        }
+    }
+    # Clean up the extraneous data
+    delete $vmhost_info->{vmprofile}{rsakey};
+    delete $vmhost_info->{vmprofile}{encryptedpassword};
+       
        $vmhost_info->{vmprofile}{vmpath} = 
$vmhost_info->{vmprofile}{datastorepath} if !$vmhost_info->{vmprofile}{vmpath};
        $vmhost_info->{vmprofile}{virtualdiskpath} = 
$vmhost_info->{vmprofile}{vmpath} if 
!$vmhost_info->{vmprofile}{virtualdiskpath};
        

Modified: vcl/trunk/mysql/update-vcl.sql
URL: 
http://svn.apache.org/viewvc/vcl/trunk/mysql/update-vcl.sql?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/mysql/update-vcl.sql (original)
+++ vcl/trunk/mysql/update-vcl.sql Mon Sep 24 20:05:17 2012
@@ -901,6 +901,9 @@ CALL AddColumnIfNotExists('vmprofile', '
 CALL AddColumnIfNotExists('vmprofile', 'datastoreimagetypeid', "smallint(5) 
unsigned NOT NULL default '1' AFTER datastorepath");
 CALL AddColumnIfNotExists('vmprofile', 'virtualswitch2', "varchar(80) NULL 
default NULL AFTER `virtualswitch1`");
 CALL AddColumnIfNotExists('vmprofile', 'virtualswitch3', "varchar(80) NULL 
default NULL AFTER `virtualswitch2`");
+CALL AddColumnIfNotExists('vmprofile', 'rsapub', "text NULL default NULL AFTER 
`virtualswitch3`");
+CALL AddColumnIfNotExists('vmprofile', 'rsakey', "varchar(256) NULL default 
NULL AFTER `rsa_pub`");
+CALL AddColumnIfNotExists('vmprofile', 'encryptedpasswd', "text NULL default 
NULL AFTER `rsa_key`");
 
 CALL AddOrRenameColumn('vmprofile', 'vmware_mac_eth0_generated', 
'eth0generated', "tinyint(1) unsigned NOT NULL default '0'");
 CALL AddOrRenameColumn('vmprofile', 'vmware_mac_eth1_generated', 
'eth1generated', "tinyint(1) unsigned NOT NULL default '0'");

Modified: vcl/trunk/mysql/vcl.sql
URL: 
http://svn.apache.org/viewvc/vcl/trunk/mysql/vcl.sql?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/mysql/vcl.sql (original)
+++ vcl/trunk/mysql/vcl.sql Mon Sep 24 20:05:17 2012
@@ -1237,6 +1237,9 @@ CREATE TABLE IF NOT EXISTS `vmprofile` (
   `password` varchar(256) NULL default NULL,
   `eth0generated` tinyint(1) unsigned NOT NULL default '0',
   `eth1generated` tinyint(1) unsigned NOT NULL default '0',
+  `rsapub` text NULL default NULL,
+  `rsakey` varchar(256) NULL default NULL,
+  `encryptedpasswd` text NULL default NULL,
   PRIMARY KEY  (`id`),
   UNIQUE KEY `profilename` (`profilename`),
   KEY `imageid` (`imageid`),

Modified: vcl/trunk/web/.ht-inc/utils.php
URL: 
http://svn.apache.org/viewvc/vcl/trunk/web/.ht-inc/utils.php?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/web/.ht-inc/utils.php (original)
+++ vcl/trunk/web/.ht-inc/utils.php Mon Sep 24 20:05:17 2012
@@ -2361,6 +2361,33 @@ function decryptData($data) {
 }
 
 
////////////////////////////////////////////////////////////////////////////////
+//
+//  \fn encryptDataAsymmetric($data, $public_key)
+//
+//  \param $data - a string
+//
+//  \param $public_key - either a filename for a public key or the public key 
itself
+//
+//  \return hex-encoded, encrypted form of $data
+//
+//  \brief generate public key encrypted data
+//
+////////////////////////////////////////////////////////////////////////////////
+function encryptDataAsymmetric($data, $public_key){
+    if(file_exists($public_key)){
+        $key = openssl_pkey_get_public(file_get_contents($public_key));
+    } else {
+        $key = openssl_pkey_get_public($public_key);
+    }    
+
+    openssl_public_encrypt($data, $encrypted, $key, 
OPENSSL_PKCS1_OAEP_PADDING);
+    openssl_free_key($key);
+
+    $hexformatted = unpack("H*hex", $encrypted);
+    return $hexformatted['hex'];
+}
+
+////////////////////////////////////////////////////////////////////////////////
 ///
 /// \fn getParentNodes($node)
 ///
@@ -9433,6 +9460,8 @@ function getVMProfiles($id="") {
               .        "vp.vmdisk, "
               .        "vp.username, "
               .        "vp.password, "
+           .        "vp.rsakey, "
+           .        "vp.rsapub, "
               .        "vp.eth0generated, "
               .        "vp.eth1generated "
               . "FROM vmprofile vp "
@@ -10757,6 +10786,7 @@ function getDojoHTML($refresh) {
                                              'dijit.form.NumberSpinner',
                                              'dijit.form.Button',
                                              'dijit.form.TextBox',
+                                  'dijit.form.Textarea',
                                              'dijit.form.FilteringSelect',
                                              'dijit.form.Select',
                                              'dijit.TitlePane',

Modified: vcl/trunk/web/.ht-inc/vm.php
URL: 
http://svn.apache.org/viewvc/vcl/trunk/web/.ht-inc/vm.php?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/web/.ht-inc/vm.php (original)
+++ vcl/trunk/web/.ht-inc/vm.php Mon Sep 24 20:05:17 2012
@@ -279,6 +279,20 @@ function editVMInfo() {
        print "    </select><img tabindex=0 src=\"images/helpicon.png\" 
id=\"genmac1help\" /></td>\n";
        print "  </tr>\n";
        print "  <tr>\n";
+    print "    <th align=right>RSA Public Key:</th>\n";
+    print "    <td>\n";
+    print "      <span id=prsapub dojoType=\"dijit.InlineEditBox\" 
editor=\"dijit.form.Textarea\" 
onChange=\"updateProfile('prsapub','rsapub')\"></span>\n";
+    print "      <img tabindex=0 src=\"images/helpicon.png\" id=\"rsapubhelp\" 
/>\n";
+    print "    </td>\n";
+    print "  </tr>\n";
+    print "  <tr>\n";
+    print "    <th align=right>RSA Private Key File:</th>\n";
+    print "    <td>\n";
+    print "      <span id=prsakey dojoType=\"dijit.InlineEditBox\" 
onChange=\"updateProfile('prsakey','rsakey');\"></span>\n";
+    print "      <img tabindex=0 src=\"images/helpicon.png\" id=\"rsakeyhelp\" 
/>\n";
+    print "    </td>\n";
+    print "  </tr>\n";
+       print "  <tr>\n";
        print "    <th align=right>Username:</th>\n";
        print "    <td><span id=pusername dojoType=\"dijit.InlineEditBox\" 
onChange=\"updateProfile('pusername', 'username');\"></span><img tabindex=0 
src=\"images/helpicon.png\" id=\"usernamehelp\" /></td>\n";
        print "  </tr>\n";
@@ -350,6 +364,12 @@ function editVMInfo() {
        print "<div dojoType=\"dijit.Tooltip\" connectId=\"genmac1help\">\n";
        print _("Specifies whether VMs are assigned MAC addresses defined in 
the VCL database or if random MAC addresses should be assigned.");
        print "</div>\n";
+    print "<div dojoType=\"dijit.Tooltip\" connectId=\"rsapubhelp\">\n";
+    print _("(Optional) In order to encrypt the VM Host password in the 
database, create an RSA public/private key pair on the relevant management 
node. Enter the public key here. Note that while this value will be available 
to every management node in your system, only those management nodes with the 
designated private key will be able to decrypt the password.");
+    print "</div>\n";
+    print "<div dojoType=\"dijit.Tooltip\" connectId=\"rsakeyhelp\">\n";
+    print _("(Optional) In order to decrypt an encrypted VM Host password, 
enter the path to a private key on the management node. Any management node 
without this private key will not be able to decrypt the password.");
+    print "</div>\n";
        print "<div dojoType=\"dijit.Tooltip\" connectId=\"usernamehelp\">\n";
        print _("Name of the administrative or root user residing on the VM 
host.");
        print "</div>\n";
@@ -872,7 +892,7 @@ function AJupdateVMprofileItem() {
        }
        $profileid = processInputVar('profileid', ARG_NUMERIC);
        $item = processInputVar('item', ARG_STRING);
-       if(! 
preg_match('/^(profilename|imageid|resourcepath|repositorypath|repositoryimagetypeid|datastorepath|datastoreimagetypeid|vmdisk|vmpath|virtualswitch[0-3]|username|password|eth0generated|eth1generated)$/',
 $item)) {
+       if(! 
preg_match('/^(profilename|imageid|resourcepath|repositorypath|repositoryimagetypeid|datastorepath|datastoreimagetypeid|vmdisk|vmpath|virtualswitch[0-3]|username|password|eth0generated|eth1generated|rsakey|rsapub)$/',
 $item)) {
                print "alert('Invalid data submitted.');";
                return;
        }
@@ -908,8 +928,19 @@ function AJupdateVMprofileItem() {
 
        $item = mysql_real_escape_string($item);
        $profile = getVMProfiles($profileid);
-       if($profile[$profileid][$item] == $newvalue)
+    if($item == 'password' && $profile[$profileid]['rsapub']){
+        $encrypted = encryptDataAsymmetric($newvalue, 
$profile[$profileid]['rsapub']);
+        $escaped = mysql_real_escape_string($encrypted);
+        $query = "UPDATE vmprofile "
+               . "SET `encryptedpasswd` = '$escaped' "
+               . "WHERE id=$profileid";
+        doQuery($query, 101);
+        # don't store the unencrypted password
+        $newvalue2 = 'NULL';
+        $newvalue = '';
+    } else if($profile[$profileid][$item] == $newvalue){
                return;
+    }
        $query = "UPDATE vmprofile "
               . "SET `$item` = $newvalue2 "
               . "WHERE id = $profileid";

Modified: vcl/trunk/web/js/vm.js
URL: 
http://svn.apache.org/viewvc/vcl/trunk/web/js/vm.js?rev=1389548&r1=1389547&r2=1389548&view=diff
==============================================================================
--- vcl/trunk/web/js/vm.js (original)
+++ vcl/trunk/web/js/vm.js Mon Sep 24 20:05:17 2012
@@ -598,6 +598,8 @@ function getVMprofileDataCB(data, ioArgs
        dijit.byId('pvs2').noValueIndicator = '(empty)';
        dijit.byId('pvs3').noValueIndicator = '(empty)';
        dijit.byId('pusername').noValueIndicator = '(empty)';
+    dijit.byId('prsakey').noValueIndicator = '(empty)';
+    dijit.byId('prsapub').noValueIndicator = '(empty)';
 
        dijit.byId('pname').setValue(curprofile.profilename);
        dijit.byId('presourcepath').setValue(curprofile.resourcepath);
@@ -611,6 +613,8 @@ function getVMprofileDataCB(data, ioArgs
        dijit.byId('pusername').setValue(curprofile.username);
        dijit.byId('pgenmac0').setValue(curprofile.eth0generated);
        dijit.byId('pgenmac1').setValue(curprofile.eth1generated);
+    dijit.byId('prsapub').setValue(curprofile.rsapub);
+    dijit.byId('prsakey').setValue(curprofile.rsakey);
        dojo.byId('ppassword').value = curprofile.password;
        dojo.byId('ppwdconfirm').value = curprofile.password;
        checkProfilePassword();
@@ -779,7 +783,7 @@ function updateProfile(id, field) {
                var newvalue = dijit.byId(id).value;
        else
                var newvalue = dojo.byId(id).value;
-       if(curprofile[field] == newvalue)
+       if(curprofile[field] == newvalue && field != 'password')
                return;
        if(field == 'password')
                dojo.byId('savestatus').innerHTML = 'Saving...';


Reply via email to