Skip to content

Commit

Permalink
WIP: prototype for EncryptionMethod::TpmFde
Browse files Browse the repository at this point in the history
  • Loading branch information
ancorgs committed Oct 30, 2023
1 parent de6c468 commit 8ea3f5e
Show file tree
Hide file tree
Showing 12 changed files with 924 additions and 7 deletions.
8 changes: 4 additions & 4 deletions package/yast2-storage-ng.spec
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ Url: https://github.com/yast/yast-storage-ng

Source: %{name}-%{version}.tar.bz2

# New Md size calculation
BuildRequires: libstorage-ng-ruby >= 4.4.76
# Encryption#use_key_file_in_commit
BuildRequires: libstorage-ng-ruby >= 4.5.144
BuildRequires: update-desktop-files
# Replace PackageSystem with Package
BuildRequires: yast2 >= 4.4.38
Expand All @@ -47,8 +47,8 @@ BuildRequires: rubygem(%{rb_default_ruby_abi}:parallel_tests)

# findutils for xargs
Requires: findutils
# New Md size calculation
Requires: libstorage-ng-ruby >= 4.4.76
# Encryption#use_key_file_in_commit
Requires: libstorage-ng-ruby >= 4.5.144
# Replace PackageSystem with Package
Requires: yast2 >= 4.4.38
# Y2Packager::Repository
Expand Down
17 changes: 17 additions & 0 deletions src/lib/y2storage/encryption.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,23 @@ class Encryption < BlkDevice
storage_forward :key_file
storage_forward :key_file=

# @!method use_key_file_in_commit?
# Whether the information at {#key_file} is used in the commit phase of libstorage-ng
# (in case it contains a valid value).
#
# The default value is true, but it can be set to false in order to fill the third column
# of the crypttab file without actually affecting the creation of the device.
#
# @return [Boolean]
storage_forward :use_key_file_in_commit?

# @!method use_key_file_in_commit=(value)
#
# Sets the {#use_key_file_in_commit?} flag
#
# @param value [Boolean]
storage_forward :use_key_file_in_commit=

# @!attribute cipher
# The encryption cipher
#
Expand Down
5 changes: 4 additions & 1 deletion src/lib/y2storage/encryption_method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
require "y2storage/encryption_method/luks1"
require "y2storage/encryption_method/pervasive_luks2"
require "y2storage/encryption_method/luks2"
require "y2storage/encryption_method/tpm_fde"
require "y2storage/encryption_method/random_swap"
require "y2storage/encryption_method/protected_swap"
require "y2storage/encryption_method/secure_swap"
Expand Down Expand Up @@ -47,6 +48,8 @@ module EncryptionMethod
PERVASIVE_LUKS2 = PervasiveLuks2.new
# Instance of the Luks2 method to be always returned by the module
LUKS2 = Luks2.new
# Instance of the TpmFde method to be always returned by the module
TPM_FDE = TpmFde.new
# Instance of the RandomSwap method to be always returned by the module
RANDOM_SWAP = RandomSwap.new
# Instance of the ProtectedSwap method to be always returned by the module
Expand All @@ -57,7 +60,7 @@ module EncryptionMethod
# Sorted list of all the method instances
# @see .all
ALL = [
LUKS1, PERVASIVE_LUKS2, LUKS2, RANDOM_SWAP, PROTECTED_SWAP, SECURE_SWAP
LUKS1, PERVASIVE_LUKS2, LUKS2, TPM_FDE, RANDOM_SWAP, PROTECTED_SWAP, SECURE_SWAP
]
private_constant :ALL

Expand Down
151 changes: 151 additions & 0 deletions src/lib/y2storage/encryption_method/tpm_fde.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Copyright (c) [2023] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.
# Copyright (c) [2019] SUSE LLC

require "yast"
require "y2storage/encryption_method/base"
require "y2storage/encryption_processes/tpm_fde_tools"

Yast.import "Mode"
Yast.import "Package"

module Y2Storage
module EncryptionMethod
# Encryption method that allows to encrypt a device using LUKS2 and configure the unlocking
# process via the system TPM using the fde-tools created by SUSE.
#
# This is a quite special encryption method due to the way the fde-tools work. First of all,
# if this method is used, it must be used at least for the root (/) filesystem and only
# additionally for some other devices.
class TpmFde < Base
def initialize
textdomain "storage"

super(:tpm_fde, _("TPM-Based Full Disk Encrytion"))
end

# @see Base#used_for?
#
# @todo Not sure if this would be possible at the end, since the exact way to setup the
# system using fde-tools is still changing too often. In any case, having a precise result
# for this method will only be relevant when implementing support for creating encrypted
# devices in an installed system. During installation returning always false is perfectly
# correct.
#
# @return [Boolean] false
def used_for?(encryption)
# One candidate criteria (still waiting for some conversations with fde-toold developers)
# could be:
# encryption.type.is?(:luks2) && key_file == EncryptionProcesses::TpmFdeTools.key_file_name
false
end

# @see Base#available?
#
# In this initial implementation this always returns false because there important
# limitations to use this in (Auto)YaST:
#
# - The current version of the inst-sys cannot talk to the TPM
# - There is still no corresponding UI in the Expert Partitioner
# - Some mechanism to ensure consistency (eg. checking all devices use the same recovery
# password) need to be introduced
# - The current implementation of the encryption method only covers system installation
# (with no support to add a new encrypted device to a system already using fde-tools)
#
# So far, the encryption method is implemented to be used by Agama (which doesn't honor
# the {#available?} method.
#
# @return [Boolean] false
def available?
false
end

# Whether both the target system and the product being installed meet the requisites
# to setup devices using this encryption method.
#
# The encryption method must be used at least for the root filesystem (eg. is not possible to
# use it for /var but not for /), but that can't hardly be controlled here. A separate
# validation that considers the whole devicegraph is needed.
#
# @return [Boolean]
def possible?
tpm_system? && tpm_product?
end

# Creates an encryption device for the given block device
#
# @param blk_device [Y2Storage::BlkDevice]
# @param dm_name [String]
#
# @return [Y2Storage::Encryption]
def create_device(blk_device, dm_name, label: "")
encryption_process.create_device(blk_device, dm_name, label: label)
end

# Special method used in the tests to reset all memoizations
def reset
@tpm_present = nil
end

private

# @see Base#encryption_process
def encryption_process
EncryptionProcesses::TpmFdeTools.new(self)
end

# Whether the system is capable of using the encryption method
#
# @see #possible?
#
# @return [Boolean]
def tpm_system?
Y2Storage::Arch.new.efiboot? && tpm_present?
end

# Whether a TPM2 chip is present and working
#
# @see #possible?
#
# @return [Boolean]
def tpm_present?
return @tpm_present unless @tpm_present.nil?

@tpm_present = EncryptionProcesses::FdeTools.new.tpm_present?
end

NEEDED_PACKAGES = ["fde-tools"].freeze
private_constant :NEEDED_PACKAGES

# Whether the product being installed has the ability to configure the encryption method
#
# @see #possible?
#
# @return [Boolean]
def tpm_product?
# TODO: We should likely do some memoization of the result. But it is not clear when
# such memoization would be invalidated (eg. new packages available due to some change
# in selected product or to new repositories).

# Beware: apart from true and false, AvailableAll can return nil if things go wrong
!!(Yast::Package.AvailableAll(NEEDED_PACKAGES))
end
end
end
end
82 changes: 82 additions & 0 deletions src/lib/y2storage/encryption_processes/fde_tools.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright (c) [2023] SUSE LLC
#
# All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of version 2 of the GNU General Public License as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, contact SUSE LLC.
#
# To contact SUSE LLC about this file by physical or electronic mail, you may
# find current contact information at www.suse.com.

require "yast2/execute"
require "y2storage/secret_attributes"

module Y2Storage
module EncryptionProcesses
# Auxiliary class to interact with the utilities provided by the fde-tools package
class FdeTools
include SecretAttributes
include Yast::Logger

# Location of the fdectl command
FDECTL = "/usr/sbin/fdectl".freeze
private_constant :FDECTL

secret_attr :recovery_password

def initialize(recovery_password = nil)
self.recovery_password = recovery_password
end

def tpm_present
Yast::Execute.on_target!(FDECTL, "tpm-present")
log.info "FDE: TPMv2 detected"
true
rescue Cheetah::ExecutionFailed
log.info "FDE: TPMv2 not detected"
false
end

alias_method :tpm_present?, :tpm_present

def add_secondary_password
command_with_password("add-secondary-password")
end

def add_secondary_key
command_with_password("add-secondary-key")
end

ENROLL_SERVICE = "fde-tpm-enroll.service".freeze
private_constant :ENROLL_SERVICE

def enroll_service
service = Yast2::Systemd::Service.find(ENROLL_SERVICE)
log.info "FDE: TPM enroll service: #{service}"
service
end

private

def command_with_password(command)
Yast::Execute.on_target!(
FDECTL, command,
stdin: "#{recovery_password}\n",
recorder: Yast::ReducedRecorder.new(skip: :stdin)
)
true
rescue Cheetah::ExecutionFailed
false
end
end
end
end

0 comments on commit 8ea3f5e

Please sign in to comment.