sub decrypt_cryptsecret()

in managementnode/lib/VCL/Module/OS/Linux/ManagementNode.pm [880:979]


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;
	
	my $private_key_file_path = $self->get_private_key_file_path();
	
	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;
		}
	}
	
	# Pass opposite of $recreate_key as $suppress_warning argument
	my $cryptsecret = get_management_node_cryptsecret_value($management_node_id, $secret_id, !$recreate_key);
	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);
	}
	
	# Override the die handler 
	local $SIG{__DIE__} = sub{};

	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 file: $private_key_file_path" . ($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;
	eval {
		$cipher = Crypt::CBC->new(
			{
				'key'				=> $key,
				'cipher'			=> 'Crypt::Rijndael',
				'iv'				=> $iv,
				'header'			=> 'none',
				'literal_key'	=> 1,
			}
		);
	};
	if (!$cipher || $EVAL_ERROR) {
		notify($ERRORS{'WARNING'}, 0, "unable to decrypt secret ID $secret_id, failed to create Crypt::CBC object" . ($EVAL_ERROR ? ", error:\n" . $EVAL_ERROR : ''));
		return;
	}
	
	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);
	}
}