Author: arkurth
Date: Fri Jun 2 21:25:53 2017
New Revision: 1797452
URL: http://svn.apache.org/viewvc?rev=1797452&view=rev
Log:
VCL-1045
Implemented code so that $self->data->get_image_domain_password() returns a
decrypted password string.
Added subroutines:
* DataStructure.pm::get_image_domain_password
* ManagementNode.pm::get_private_key_file_path
* ManagementNode.pm::_get_private_key_object_from_file
* ManagementNode.pm::extract_public_key_from_private_key_file
* ManagementNode.pm::check_encryption_keys
* ManagementNode.pm::generate_private_key_file
* ManagementNode.pm::decrypt_cryptsecret
* utils.pm::get_management_node_cryptkey_pubkey
* utils.pm::set_management_node_cryptkey_pubkey
* utils.pm::get_management_node_cryptsecret_info
* utils.pm::get_management_node_cryptsecret_value
* utils.pm::delete_management_node_cryptsecret
* utils.pm::update_reservation_cryptsecret
Added cryptkey/crypsecret data to return value from
utils.pm::get_image_active_directory_domain_info. (This is mainly for
debugging, the values from this hash aren't used.)
Added perl-Crypt-CBC to install_perl_libs.pl, now used by ManagementNode.pm.
Updated DataStructure.pm::SUBROUTINE_MAPPINGS to match current schema for AD
tables.
Modified:
vcl/trunk/managementnode/bin/install_perl_libs.pl
vcl/trunk/managementnode/lib/VCL/DataStructure.pm
vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/ManagementNode.pm
vcl/trunk/managementnode/lib/VCL/utils.pm
Modified: vcl/trunk/managementnode/bin/install_perl_libs.pl
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/bin/install_perl_libs.pl?rev=1797452&r1=1797451&r2=1797452&view=diff
==============================================================================
--- vcl/trunk/managementnode/bin/install_perl_libs.pl (original)
+++ vcl/trunk/managementnode/bin/install_perl_libs.pl Fri Jun 2 21:25:53 2017
@@ -57,6 +57,7 @@ my @LINUX_PACKAGES = (
'openssl-devel',
'perl-Archive-Tar',
'perl-CPAN',
+ 'perl-Crypt-CBC',
'perl-Crypt-OpenSSL-RSA',
'perl-DBD-MySQL',
'perl-DBI',
Modified: vcl/trunk/managementnode/lib/VCL/DataStructure.pm
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/DataStructure.pm?rev=1797452&r1=1797451&r2=1797452&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/DataStructure.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/DataStructure.pm Fri Jun 2 21:25:53 2017
@@ -394,15 +394,17 @@ $SUBROUTINE_MAPPINGS{imagemeta_subimages
$SUBROUTINE_MAPPINGS{imagemeta_sysprep} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagemeta}{sysprep}';
$SUBROUTINE_MAPPINGS{imagemeta_rootaccess} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagemeta}{rootaccess}';
$SUBROUTINE_MAPPINGS{imagemeta_sethostname} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagemeta}{sethostname}';
-
-$SUBROUTINE_MAPPINGS{image_domain_dns_servers} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{dnsServers}';
+
+#$SUBROUTINE_MAPPINGS{image_domain_dns_servers} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{dnsServers}';
# Explicit subroutine
$SUBROUTINE_MAPPINGS{image_domain_dns_name} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{domainDNSName}';
$SUBROUTINE_MAPPINGS{image_domain_id} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{id}';
-$SUBROUTINE_MAPPINGS{image_domain_login_description} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{logindescription}';
-$SUBROUTINE_MAPPINGS{image_domain_password} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{password}';
-$SUBROUTINE_MAPPINGS{image_domain_prettyname} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{prettyname}';
+$SUBROUTINE_MAPPINGS{image_domain_name} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{name}';
+$SUBROUTINE_MAPPINGS{image_domain_owner_id} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{ownerid}';
+#$SUBROUTINE_MAPPINGS{image_domain_password} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{password}';
# Explicit subroutine
+$SUBROUTINE_MAPPINGS{image_domain_secret_id} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{secretid}';
$SUBROUTINE_MAPPINGS{image_domain_username} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{username}';
$SUBROUTINE_MAPPINGS{image_domain_base_ou} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{imageaddomain}{baseOU}';
+$SUBROUTINE_MAPPINGS{image_domain_cryptsecret} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{imagedomain}{cryptsecret}{cryptsecret}';
$SUBROUTINE_MAPPINGS{image_os_name} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{OS}{name}';
$SUBROUTINE_MAPPINGS{image_os_prettyname} =
'$self->request_data->{reservation}{RESERVATION_ID}{image}{OS}{prettyname}';
@@ -2765,6 +2767,40 @@ sub get_invalid_substitution_identifiers
}
#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_image_domain_password
+
+ Parameters : none
+ Returns : string
+ Description : Returns the decrypted Active Directory domain password.
+
+=cut
+
+sub get_image_domain_password {
+ my $self = shift;
+ if (ref($self) !~ /VCL::/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return 0;
+ }
+
+ my $reservation_id = $self->reservation_id();
+
+ my $secret_id = $self->get_image_domain_secret_id();
+ if (!defined($secret_id)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to retrieve decrypted
domain password, addomain.secretid is not defined in this DataStructure.pm
object");
+ return;
+ }
+
+ my $encrypted_password =
$self->request_data->{reservation}{$reservation_id}{image}{imagedomain}{password};
+ if (!defined($encrypted_password)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to retrieve decrypted
domain password, imagedomain.password is not defined in this DataStructure.pm
object");
+ return;
+ }
+
+ return $self->mn_os->decrypt_cryptsecret($secret_id,
$encrypted_password);
+}
+
+#//////////////////////////////////////////////////////////////////////////////
1;
__END__
Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/ManagementNode.pm
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/ManagementNode.pm?rev=1797452&r1=1797451&r2=1797452&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/ManagementNode.pm
(original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Linux/ManagementNode.pm Fri Jun
2 21:25:53 2017
@@ -55,6 +55,12 @@ use diagnostics;
use VCL::utils;
+use Crypt::CBC;
+use Crypt::OpenSSL::RSA;
+use English;
+use File::Basename;
+use MIME::Base64;
+
###############################################################################
=head1 CLASS VARIABLES
@@ -75,6 +81,15 @@ use VCL::utils;
our $MN_STAGE_SCRIPTS_DIRECTORY = "$TOOLS/ManagementNode/Scripts";
+=head2 $MN_PRIVATE_ENCRYPTION_KEY_FILE_PATH
+
+ Data type : String
+ Description : /root/.vcl/<FQDN>.key
+
+=cut
+
+our $MN_PRIVATE_ENCRYPTION_KEY_FILE_PATH = "/root/.vcl/$FQDN.key";
+
###############################################################################
=head1 OBJECT METHODS
@@ -261,7 +276,7 @@ sub create_text_file {
=head2 get_file_contents
- Parameters : $file_path
+ Parameters : $file_path, $display_warnings (optional)
Returns : array or string
Description : Retrieves the contents of a file on the management node.
@@ -274,16 +289,17 @@ sub get_file_contents {
return;
}
- my ($file_path) = @_;
+ my ($file_path, $display_warnings) = @_;
if (!defined($file_path)) {
notify($ERRORS{'WARNING'}, 0, "file path argument was not
supplied");
return;
}
+ $display_warnings = 1 unless defined($display_warnings);
my $computer_node_name = $self->data->get_computer_node_name();
if (!open FILE, '<', $file_path) {
- notify($ERRORS{'WARNING'}, 0, "failed to retrieve contents of
file on $computer_node_name, file could not be opened: $file_path");
+ notify($ERRORS{'WARNING'}, 0, "failed to retrieve contents of
file on $computer_node_name, file could not be opened: $file_path") if
$display_warnings;
return;
}
my @lines = <FILE>;
@@ -604,6 +620,380 @@ sub setup_get_menu {
}
#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_private_key_file_path
+
+ Parameters : none
+ Returns : string
+ Description : Returns the location on the management node where the private
key
+ resides that is used to decrypt secrets: /root/.vcl/<FQDN>.key
+
+=cut
+
+sub get_private_key_file_path {
+ return $MN_PRIVATE_ENCRYPTION_KEY_FILE_PATH;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 _get_private_key_object_from_file
+
+ Parameters : none
+ Returns : string
+ Description : Retrieves the private key string from the file on the management
+ node and creates a Crypt::OpenSSL::RSA object based on it.
+
+=cut
+
+sub _get_private_key_object_from_file {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return;
+ }
+
+ return $self->{private_key_object_from_file} if
defined($self->{private_key_object_from_file});
+
+ my $management_node_short_name =
$self->data->get_management_node_short_name() || return;
+
+ my $private_key_file_path = $self->get_private_key_file_path();
+ if (!$self->file_exists($private_key_file_path)) {
+ notify($ERRORS{'OK'}, 0, "unable to retrieve private key from
file on $management_node_short_name because file does NOT exist:
$private_key_file_path");
+ return;
+ }
+
+ my $private_key_file_string =
$self->get_file_contents($private_key_file_path);
+ if (!$private_key_file_string) {
+ notify($ERRORS{'WARNING'}, 0, "failed to retrieve private key
from file on $management_node_short_name: $private_key_file_path");
+ return;
+ }
+
+ # Create an RSA object based on the existing private key contained in
the file
+ my $rsa_private;
+ eval {
+ $rsa_private =
Crypt::OpenSSL::RSA->new_private_key($private_key_file_string);
+ };
+ if ($EVAL_ERROR || !$rsa_private) {
+ notify($ERRORS{'WARNING'}, 0, "failed to create private key
file Crypt::OpenSSL::RSA object from $private_key_file_path on
$management_node_short_name" . ($EVAL_ERROR ? ", error:\n" . $EVAL_ERROR : ''));
+ return;
+ }
+
+ $self->{private_key_object_from_file} = $rsa_private;
+ return $rsa_private;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 extract_public_key_from_private_key_file
+
+ Parameters : none
+ Returns : string
+ Description : Retrieves the private key from the file on the management node
+ and extracts the public key from the private key. The public key
+ string is returned.
+
+=cut
+
+sub extract_public_key_from_private_key_file {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return;
+ }
+
+ my $rsa_private = $self->_get_private_key_object_from_file() || return;
+
+ my $management_node_short_name =
$self->data->get_management_node_short_name();
+ my $private_key_file_path = $self->get_private_key_file_path();
+
+ # Retrieve the public key string from the RSA object
+ my $public_key_string;
+ eval {
+ $public_key_string = $rsa_private->get_public_key_x509_string();
+ };
+ if ($EVAL_ERROR || !$public_key_string) {
+ notify($ERRORS{'WARNING'}, 0, "failed to extract public key
from private key file $private_key_file_path on $management_node_short_name,
failed to retrieve public key from private key contained in the file" .
($EVAL_ERROR ? ", error:\n" . $EVAL_ERROR : ''));
+ return;
+ }
+
+ return $public_key_string;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 check_encryption_keys
+
+ Parameters : none
+ Returns : string
+ Description : Retrieves the cryptkeys.pubkey value from the database for the
+ management node and extracts the public key from the private key
+ file on the management node. Returns true if they match. Returns
+ false if they differ or if either could not be retrieved.
+
+=cut
+
+sub check_encryption_keys {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return;
+ }
+
+ my $management_node_id = $self->data->get_management_node_id() ||
return;
+ my $management_node_short_name =
$self->data->get_management_node_short_name() || return;
+
+ notify($ERRORS{'DEBUG'}, 0, "*** checking encryption keys on
$management_node_short_name ***");
+
+ # Get the cryptkey.pubkey value from the database for the management
node
+ my $public_key_string_database_value =
get_management_node_cryptkey_pubkey($management_node_id, 0) || return;
+
+ # Create an RSA object based on the existing public key stored in the
database, then extract the (hopefully same) public key from the object
+ # Do this to verify the public key is correctly formatted, the RSA
module should strip any extraneous space or newlines
+ my $rsa_public;
+ eval {
+ $rsa_public =
Crypt::OpenSSL::RSA->new_public_key($public_key_string_database_value);
+ };
+ if ($EVAL_ERROR || !$rsa_public) {
+ notify($ERRORS{'WARNING'}, 0, "failed to create RSA object from
public key stored in database:\n$public_key_string_database_value" .
($EVAL_ERROR ? ", error:\n" . $EVAL_ERROR : ''));
+ return;
+ }
+
+ # Retrieve the public key string from the RSA object
+ my $public_key_string_database_extracted;
+ eval {
+ $public_key_string_database_extracted =
$rsa_public->get_public_key_x509_string();
+ };
+ if ($EVAL_ERROR || !$public_key_string_database_extracted) {
+ notify($ERRORS{'WARNING'}, 0, "retrieved cryptkey.pubkey value
from database, created RSA object based on this public key, but failed to
extract the public key from the object, there may be a problem with the public
key stored in the database:\n$public_key_string_database_value" . ($EVAL_ERROR
? ", error:\n" . $EVAL_ERROR : ''));
+ return;
+ }
+
+ # Extract the public key string from the private key file stored on the
management node
+ my $public_key_string_private_extracted =
$self->extract_public_key_from_private_key_file() || return;
+
+ if ($public_key_string_database_extracted eq
$public_key_string_private_extracted) {
+ notify($ERRORS{'OK'}, 0, "public key extracted from private key
file on $management_node_short_name matches database cryptkey.pubkey value");
+ return 1;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "public key extracted from
private key file on $management_node_short_name does not match database
cryptkey.pubkey value:\n" .
+ "public key stored in database (cryptkey.pubkey):\n" .
string_to_ascii($public_key_string_database_extracted) . "\n" .
+ "public key extracted from private key file:\n" .
string_to_ascii($public_key_string_private_extracted)
+ );
+ return 0;
+ }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 generate_private_key_file
+
+ Parameters : none
+ Returns : boolean
+ Description : Creates a 4096 bit RSA private key file on the management node
+ at /root/.vcl/<FQDN>.key.
+
+=cut
+
+sub generate_private_key_file {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return;
+ }
+
+ my $arguments_hash_ref = shift;
+ if (defined($arguments_hash_ref) && !ref($arguments_hash_ref) ||
ref($arguments_hash_ref) ne 'HASH') {
+ notify($ERRORS{'WARNING'}, 0, "argument was supplied but is not
a hash reference:\n" . format_data($arguments_hash_ref));
+ return;
+ }
+
+ my $private_key_file_path = $self->get_private_key_file_path();
+ my $bits = 4096;
+
+ # If provided and true, existing private key file will be deleted if it
exists
+ my $force = $arguments_hash_ref->{force};
+
+ my $management_node_id = $self->data->get_management_node_id() ||
return;
+ my $management_node_short_name =
$self->data->get_management_node_short_name() || return;
+ my $reservation_id = $self->data->get_reservation_id();
+
+ notify($ERRORS{'DEBUG'}, 0, "*** attempting to generate a new private
key file on $management_node_short_name: $private_key_file_path ***");
+
+ # Make sure the private key file does not already exist
+ if ($self->file_exists($private_key_file_path)) {
+ if ($force) {
+ notify($ERRORS{'OK'}, 0, "force argument was specified,
existing private key file will be overwritten: $private_key_file_path");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to generate
encryption keys, private key file already exists: $private_key_file_path");
+ return;
+ }
+ }
+
+ # Delete cached RSA object
+ if ($self->{private_key_object_from_file}) {
+ notify($ERRORS{'DEBUG'}, 0, "deleting cached RSA private key
object");
+ delete $self->{private_key_object_from_file};
+ }
+
+ # Delete all existing cryptsecret entries for the management node
+ # The website's API won't delete any that may have been created with an
earlier key
+ delete_management_node_cryptsecret($management_node_id);
+
+ # Create a new RSA object containing a private/public key pair
+ # Create an RSA object based on the existing private key contained in
the file
+ my $rsa_generate;
+ eval {
+ $rsa_generate = Crypt::OpenSSL::RSA->generate_key($bits);
+ };
+ if ($EVAL_ERROR || !$rsa_generate) {
+ notify($ERRORS{'WARNING'}, 0, "failed to create private key
file on management node $management_node_short_name: $private_key_file_path,
RSA object could not be created" . ($EVAL_ERROR ? ", error:\n" . $EVAL_ERROR :
''));
+ return;
+ }
+
+ my $private_key_string;
+ eval {
+ $private_key_string = $rsa_generate->get_private_key_string();
+ };
+ if ($EVAL_ERROR || !$private_key_string) {
+ notify($ERRORS{'WARNING'}, 0, "failed to create private key
file on management node $management_node_short_name: $private_key_file_path,
private key string could not be retireved from RSA object" . ($EVAL_ERROR ? ",
error:\n" . $EVAL_ERROR : ''));
+ return;
+ }
+
+ my $public_key_string;
+ eval {
+ $public_key_string =
$rsa_generate->get_public_key_x509_string();
+ };
+ if ($EVAL_ERROR || !$public_key_string) {
+ notify($ERRORS{'WARNING'}, 0, "failed to create private key
file on management node $management_node_short_name: $private_key_file_path,
public key string could not be retireved from RSA object" . ($EVAL_ERROR ? ",
error:\n" . $EVAL_ERROR : ''));
+ return;
+ }
+
+ $self->create_text_file($private_key_file_path, $private_key_string) ||
return;
+
+ # Update cryptkey table with the public key string
+ if (!set_management_node_cryptkey_pubkey($management_node_id,
$public_key_string)) {
+ notify($ERRORS{'WARNING'}, 0, "created private key file on
management node $management_node_short_name: $private_key_file_path, failed to
update cryptkey table in database, attempting to delete private key file just
created: $private_key_file_path");
+ $self->delete_file($private_key_file_path);
+ return;
+ }
+
+ # Call the XML-RPC API to create a new cryptsecret entry for this
reservation
+ update_reservation_cryptsecret($reservation_id);
+
+ notify($ERRORS{'OK'}, 0, "created private key file on management node
$management_node_short_name: $private_key_file_path, updated cryptkey.pubkey
value in database");
+ return 1;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 decrypt_cryptsecret
+
+ Parameters : $secret_id, $encrypted_string
+ Returns : string
+ Description : Decrypts an encrypted string stored in the database such as
+ addomain.password.
+
+ The $secret_id argument must match a cryptsecret.secretid value
+ in the database. The corresponding cryptsecret.cryptsecret value
+ is a base64-encoded string that is encrypted using the
management
+ node's public key stored in cryptkey.pubkey. The management
+ node's private key is used to decrypt it.
+
+=cut
+
+sub decrypt_cryptsecret {
+ my $self = shift;
+ if (ref($self) !~ /VCL::Module/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return;
+ }
+
+ my ($secret_id, $encrypted_string, $recreate_key) = @_;
+ if (!defined($secret_id)) {
+ notify($ERRORS{'WARNING'}, 0, "secret ID argument was not
supplied");
+ return;
+ }
+ if (!defined($encrypted_string)) {
+ notify($ERRORS{'WARNING'}, 0, "encrypted string argument was
not supplied");
+ return;
+ }
+
+ my $management_node_id = $self->data->get_management_node_id() ||
return;
+ my $management_node_short_name =
$self->data->get_management_node_short_name() || return;
+
+ if ($recreate_key) {
+ notify($ERRORS{'DEBUG'}, 0, "previous attempt to decrypt
cryptsecret failed, attempting to regenerate private key and cryptsecret
entries");
+ if (!$self->generate_private_key_file({force => 1})) {
+ notify($ERRORS{'WARNING'}, 0, "unable to decrypt secret
ID $secret_id, failed to verify private key stored in file on management node
is valid and its public key matches the cryptkey.pubkey value in the database");
+ return;
+ }
+ }
+ elsif (!$self->check_encryption_keys()) {
+ return $self->decrypt_cryptsecret($secret_id,
$encrypted_string, 1);
+ }
+
+ my $cryptsecret =
get_management_node_cryptsecret_value($management_node_id, $secret_id);
+ if (!$cryptsecret) {
+ notify($ERRORS{'WARNING'}, 0, "unable to decrypt secret ID
$secret_id, failed to retrieve cryptsecret.cryptsecret value for management
node ID $management_node_id");
+ $recreate_key ? return : return
$self->decrypt_cryptsecret($secret_id, $encrypted_string, 1);
+ }
+
+ # The encrypted string (addomain.password, etc) and
cryptsecret.cryptsecret should ALWAYS be base64 encoded
+ # They must be decoded to binary before being passed to decrypt
functions
+ my $encrypted_string_decoded = decode_base64($encrypted_string);
+ my $cryptsecret_decoded = decode_base64($cryptsecret);
+
+ my $rsa_private = $self->_get_private_key_object_from_file();
+ if (!$rsa_private) {
+ notify($ERRORS{'WARNING'}, 0, "unable to decrypt secret ID
$secret_id, failed to create RSA object based on management node's private
key");
+ $recreate_key ? return : return
$self->decrypt_cryptsecret($secret_id, $encrypted_string, 1);
+ }
+
+ my $key;
+ eval {
+ $key = $rsa_private->decrypt($cryptsecret_decoded);
+ };
+ if ($EVAL_ERROR || !$key) {
+ # Wrong key error:
+ # RSA.xs:202: OpenSSL error: oaep decoding error
+ notify($ERRORS{'WARNING'}, 0, "unable to decrypt secret ID
$secret_id, failed to decrypt cryptsecret using RSA object based on management
node's private key" . ($EVAL_ERROR ? ", error:\n" . $EVAL_ERROR : ''));
+ $recreate_key ? return : return
$self->decrypt_cryptsecret($secret_id, $encrypted_string, 1);
+ }
+
+ my $encrypted_string_length = length($encrypted_string_decoded);
+ if ($encrypted_string_length < 32) {
+ # This should always be at least 32 bytes
+ # If less than 16, the next 2 substr commands may fail with
'substr outside of string' errors
+ notify($ERRORS{'WARNING'}, 0, "unable to decrypt secret ID
$secret_id, encrypted string length: $encrypted_string_length bytes, it must be
32 bytes or more:\n$encrypted_string\n\n" . decode_base64($encrypted_string));
+ return;
+ }
+
+ my $iv = substr($encrypted_string_decoded, 0, 16);
+ my $ciphered_string = substr($encrypted_string_decoded, 16);
+
+ my $cipher = Crypt::CBC->new(
+ {
+ 'key' => $key,
+ 'cipher' =>
'Crypt::OpenSSL::AES',
+ 'iv' => $iv,
+ 'header' => 'none',
+ 'literal_key' => 1,
+ }
+ );
+ my $decrypted_string = $cipher->decrypt($ciphered_string);
+ if (defined($decrypted_string)) {
+ notify($ERRORS{'OK'}, 0, "decrypted secret ID $secret_id");
+ return $decrypted_string;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to decrypt secret ID
$secret_id");
+ $recreate_key ? return : return
$self->decrypt_cryptsecret($secret_id, $encrypted_string, 1);
+ }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
1;
__END__
Modified: vcl/trunk/managementnode/lib/VCL/utils.pm
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=1797452&r1=1797451&r2=1797452&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/utils.pm Fri Jun 2 21:25:53 2017
@@ -107,6 +107,7 @@ our @EXPORT = qw(
database_execute
database_select
delete_computerloadlog_reservation
+ delete_management_node_cryptsecret
delete_request
delete_reservation_account
delete_variable
@@ -165,6 +166,9 @@ our @EXPORT = qw(
get_management_node_computer_ids
get_management_node_id
get_management_node_info
+ get_management_node_cryptkey_pubkey
+ get_management_node_cryptsecret_info
+ get_management_node_cryptsecret_value
get_management_node_requests
get_management_node_vmhost_ids
get_management_node_vmhost_info
@@ -243,6 +247,7 @@ our @EXPORT = qw(
run_scp_command
run_ssh_command
set_logfile_path
+ set_management_node_cryptkey_pubkey
set_managementnode_state
set_reservation_lastcheck
set_variable
@@ -288,6 +293,7 @@ our @EXPORT = qw(
update_preload_flag
update_request_checkuser
update_request_state
+ update_reservation_cryptsecret
update_reservation_lastcheck
update_reservation_natlog
update_reservation_password
@@ -14511,12 +14517,16 @@ sub get_image_active_directory_domain_in
return;
}
+ my $management_node_id = get_management_node_id();
+
# Get a hash ref containing the database column names
my $database_table_columns = get_database_table_columns();
my @tables = (
'addomain',
'imageaddomain',
+ 'cryptsecret',
+ #'cryptkey',
);
# Construct the select statement
@@ -14538,6 +14548,12 @@ sub get_image_active_directory_domain_in
FROM
imageaddomain,
addomain
+LEFT JOIN (cryptsecret, cryptkey) ON (
+ addomain.secretid = cryptsecret.secretid AND
+ cryptsecret.cryptkeyid = cryptkey.id AND
+ cryptkey.hosttype = 'managementnode' AND
+ cryptkey.hostid = $management_node_id
+)
WHERE
imageaddomain.imageid = $image_id
AND imageaddomain.addomainid = addomain.id
@@ -14573,6 +14589,9 @@ EOF
if ($table eq $tables[0]) {
$info->{$column} = $value;
}
+ elsif ($table eq 'cryptkey') {
+ $info->{cryptsecret}{$table}{$column} = $value;
+ }
else {
$info->{$table}{$column} = $value;
}
@@ -14894,6 +14913,270 @@ EOF
return 1;
}
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_management_node_cryptkey_pubkey
+
+ Parameters : $management_node_id, $show_warnings (optional)
+ Returns : string
+ Description : Retrieves the cryptkey.pubkey value for the management node.
+
+=cut
+
+sub get_management_node_cryptkey_pubkey {
+ my ($management_node_id, $show_warnings) = @_;
+ if (!defined($management_node_id)) {
+ notify($ERRORS{'WARNING'}, 0, "management node ID argument was
not supplied");
+ return;
+ }
+
+ $show_warnings = 1 unless defined($show_warnings);
+
+ my $select_statement = <<EOF;
+SELECT
+cryptkey.pubkey
+FROM
+cryptkey
+WHERE
+cryptkey.hosttype = 'managementnode'
+AND cryptkey.hostid = $management_node_id
+EOF
+
+ my @rows = database_select($select_statement);
+ if (!@rows) {
+ if ($show_warnings) {
+ notify($ERRORS{'WARNING'}, 0, "cryptkey table does NOT
contain a row for management node ID $management_node_id");
+ }
+ else {
+ notify($ERRORS{'DEBUG'}, 0, "cryptkey table does NOT
contain a row for management node ID $management_node_id");
+ }
+ return;
+ }
+
+ my $row = $rows[0];
+ my $public_key_string = $row->{pubkey};
+ notify($ERRORS{'DEBUG'}, 0, "retrieved public key from cryptkey table
for management node ID $management_node_id");
+ return $public_key_string;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 set_management_node_cryptkey_pubkey
+
+ Parameters : $host_id, $public_key_string
+ Returns : $cryptkey_id
+ Description : Set or updates the cryptkey.pubkey value for the management
node.
+
+=cut
+
+sub set_management_node_cryptkey_pubkey {
+ my ($management_node_id, $public_key_string) = @_;
+ if (!defined($management_node_id)) {
+ notify($ERRORS{'WARNING'}, 0, "management node ID argument was
not supplied");
+ return;
+ }
+ elsif (!defined($public_key_string)) {
+ notify($ERRORS{'WARNING'}, 0, "public key string argument was
not supplied");
+ return;
+ }
+
+ my $insert_statement = <<EOF;
+INSERT INTO cryptkey
+(hostid, hosttype, pubkey)
+VALUES
+(
+ $management_node_id,
+ 'managementnode',
+ '$public_key_string'
+)
+ON DUPLICATE KEY UPDATE
+pubkey='$public_key_string'
+EOF
+
+ my $cryptkey_id = database_execute($insert_statement);
+ if ($cryptkey_id) {
+ notify($ERRORS{'DEBUG'}, 0, "set public key in cryptkey table
for management node ID $management_node_id, cryptkey ID");
+ return 1;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to execute insert
statement to set public key in cryptkey table for management node ID
$management_node_id");
+ return;
+ }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_management_node_cryptsecret_info
+
+ Parameters : $management_node_id
+ Returns : hash reference
+ Description :
+
+=cut
+
+sub get_management_node_cryptsecret_info {
+ my ($management_node_id) = @_;
+ if (!defined($management_node_id)) {
+ notify($ERRORS{'WARNING'}, 0, "management node ID argument was
not supplied");
+ return;
+ }
+
+ my $select_statement = <<EOF;
+SELECT
+cryptsecret.*
+FROM
+cryptsecret,
+cryptkey
+WHERE
+cryptkey.hostid = $management_node_id
+AND cryptkey.hosttype = 'managementnode'
+AND cryptsecret.cryptkeyid = cryptkey.id
+EOF
+
+ my @rows = database_select($select_statement);
+ if (scalar @rows == 0) {
+ notify($ERRORS{'WARNING'}, 0, "failed to retrieve cryptsecret
info from database for management node $management_node_id");
+ return;
+ }
+
+ print format_data(\@rows) . "\n\n";
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_management_node_cryptsecret_value
+
+ Parameters : $management_node_id, $secret_id
+ Returns : boolean
+ Description : Retrieves the cryptsecret.cryptsecret value matching the
+ cryptsecret.secretid value from the database for the management
+ node.
+
+=cut
+
+sub get_management_node_cryptsecret_value {
+ my ($management_node_id, $secret_id) = @_;
+ if (!defined($management_node_id)) {
+ notify($ERRORS{'WARNING'}, 0, "management node ID argument was
not supplied");
+ return;
+ }
+ if (!defined($secret_id)) {
+ notify($ERRORS{'WARNING'}, 0, "secret ID argument was not
supplied");
+ return;
+ }
+
+ my $select_statement = <<EOF;
+SELECT
+cryptsecret.cryptsecret
+FROM
+cryptsecret,
+cryptkey
+WHERE
+cryptkey.hostid = $management_node_id
+AND cryptkey.hosttype = 'managementnode'
+AND cryptsecret.secretid = $secret_id
+AND cryptsecret.cryptkeyid = cryptkey.id
+EOF
+
+ my @rows = database_select($select_statement);
+ if (scalar @rows == 0) {
+ notify($ERRORS{'WARNING'}, 0, "failed to retrieve cryptsecret
from database for management node $management_node_id, secret ID: $secret_id");
+ return;
+ }
+
+ my $cryptsecret = $rows[0]->{cryptsecret};
+ notify($ERRORS{'DEBUG'}, 0, "retrieved cryptsecret, management node ID:
$management_node_id, secret ID: $secret_id");
+ return $cryptsecret;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 delete_management_node_cryptsecret
+
+ Parameters : $management_node_id, $secret_id (optional)
+ Returns : boolean
+ Description :
+
+=cut
+
+sub delete_management_node_cryptsecret {
+ my ($management_node_id, $secret_id) = @_;
+ if (!defined($management_node_id)) {
+ notify($ERRORS{'WARNING'}, 0, "management node ID argument was
not supplied");
+ return;
+ }
+
+ my $delete_statement = <<EOF;
+DELETE
+cryptsecret.*
+FROM
+cryptsecret,
+cryptkey
+WHERE
+cryptkey.hostid = $management_node_id
+AND cryptkey.hosttype = 'managementnode'
+AND cryptsecret.cryptkeyid = cryptkey.id
+EOF
+ if ($secret_id) {
+ $delete_statement .= "AND cryptsecret.secretid = $secret_id";
+ }
+
+ if (database_execute($delete_statement)) {
+ notify($ERRORS{'OK'}, 0, "deleted entries from cryptsecret
table for management node ID $management_node_id" . ($secret_id ? ", secret ID:
$secret_id" : ''));
+ return 1;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to delete entries from
cryptsecret table for management node ID $management_node_id" . ($secret_id ?
", secret ID: $secret_id" : ''));
+ return 1;
+ }
+}
+
+#//////////////////////////////////////////////////////////////////////////////
+
+=head2 update_reservation_cryptsecret
+
+ Parameters : none
+ Returns : boolean
+ Description : Calls the XML-RPC XMLRPCupdateSecrets function to update the
+ cryptsecret table for the reservation.
+
+=cut
+
+sub update_reservation_cryptsecret {
+ my $reservation_id = shift;
+ if (!defined($reservation_id)) {
+ notify($ERRORS{'WARNING'}, 0, "reservation ID argument was not
supplied");
+ return;
+ }
+
+ my $xmlrpc_function = 'XMLRPCupdateSecrets';
+ my @xmlrpc_arguments = (
+ $xmlrpc_function,
+ $reservation_id
+ );
+
+ my $response = xmlrpc_call(@xmlrpc_arguments);
+ if (!defined($response)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to update cryptsecret
table, $xmlrpc_function returned undefined");
+ return;
+ }
+ elsif ($response->value->{status} =~ /success/) {
+ notify($ERRORS{'OK'}, 0, "called XMLRPCupdateSecrets,
cryptsecret table successfully updated");
+ }
+ elsif ($response->value->{status} =~ /noupdate/) {
+ notify($ERRORS{'OK'}, 0, "called XMLRPCupdateSecrets,
cryptsecret table does not need to be updated");
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "failed to update cryptsecret
table, $xmlrpc_function returned:\n" .
+ "status : $response->value->{status}\n" .
+ "error code : $response->value->{errorcode}\n" .
+ "error message : $response->value->{errormsg}"
+ );
+ return;
+ }
+ return 1;
+}
+
#//////////////////////////////////////////////////////////////////////////////