vcl-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From arku...@apache.org
Subject svn commit: r1797452 - in /vcl/trunk/managementnode: bin/install_perl_libs.pl lib/VCL/DataStructure.pm lib/VCL/Module/OS/Linux/ManagementNode.pm lib/VCL/utils.pm
Date Fri, 02 Jun 2017 21:25:53 GMT
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;
+}
+
 #//////////////////////////////////////////////////////////////////////////////
 
 



Mime
View raw message